CodeGym /Java Course /Java Collections /Design patterns: singleton, factory, factory method, abst...

Design patterns: singleton, factory, factory method, abstract factory

Java Collections
Level 7 , Lesson 1
Available

"Hi, Amigo!"

"Hi, Bilaabo!"

"Our topic today won't merely be interesting — it will be downright epic."

"Today I'm going to you what design patterns are."

"Cool! I've heard a lot about them. I can't wait!"

"Experienced programmers have to write a lot of classes. But the most difficult part of this job is deciding which classes to create and how to divide the work among them."

"The more they solved such questions, the more they realized that some solutions are good, while others are bad."

"Bad solutions usually create more problems than they solve. They extend poorly, create many unnecessary limitations, etc. And good solutions are the opposite."

"Is there some analogy you can make?"

"Let's say you're building a house. And you're thinking of what it will be made of. You decide you need walls, a floor, and a ceiling. As a result, you build a house with a flat roof and no foundation. Such a house will crack, and the roof will leak. This is a bad solution."

"Conversely, a house consisting of a foundation, walls, and a gable roof would be a good solution. Heavy snowfall presents no problem, since the snow will slide off the roof. And shifting soils are nothing to fear, because the foundation will ensure stability. We would call such a solution good."

"I see. Thanks."

"OK. Then I will continue."

"In time, the good solutions came to be known as design patterns, while the bad ones were called anti-patterns."

"A design pattern is like an answer to a question. It's difficult to understand if you've never heard the question."

"The first category of patterns is creational patterns. Such patterns describe good solutions related to the creation of objects."

"What's so complicated about creating objects?"

"As it happens, that's just what we're going to explore now."

Singleton pattern.

Design patterns: singleton, factory, factory method, abstract factory - 1

"Often in programs, only a single copy of some objects can exist. For example, the console, logger, garbage collector, etc."

"Bad solution: don't create any objects — just create a class whose methods are all static."

"Good solution: create a single object and store it in a static variable. Prevent the creation of a second object of the class. For example:"

Example
class Sun
{
 private static Sun instance;
 public static Sun getInstance()
 {
  if (instance == null)
  instance = new Sun();

  return instance;
 }

 private Sun()
 {
 }
}
How it is called
Sun sun = Sun.getInstance();

"It's simple."

"First, we make the constructor private. Now it can only be called from inside our class. We've blocked creation of Sun objects everywhere except within methods of the Sun class."

"Second, an object of this class can only be obtained by calling the getInstance() method. Not only is this the only method that can create a Sun object, it also ensures that there is only one such object."

"I see."

"When you think, «Now, how exactly would I do that?», a pattern says, «you can try this — this is one of the best solutions.»"

"Thanks. Now things are starting to become clear."

"You can also read about this pattern here."

Factory pattern.

Design patterns: singleton, factory, factory method, abstract factory - 2

"Here's a situation that programmers face very often. You have some base class and many subclasses. For example, a game character class (GamePerson) and classes for all the other characters which inherit it."

"Let's say you have the following classes:"

Example
abstract class GamePerson
{
}

class Warrior extends GamePerson
{
}

class Mag extends GamePerson
{
}

class Troll extends GamePerson
{
}

class Elf extends GamePerson
{
}

"The question is how do we flexibly and conveniently manage the creation of objects of these classes."

"If this problem seems far-fetched to you, imagine that in the game you need to create dozens of swords and shields, hundreds of magic spells, and thousands of monsters. You can't do without a convenient approach to object creation here."

"The Factory pattern offers a good solution."

"First, we need to create an enum whose values correspond to the various classes."

"Second, create a special Factory class that has static method(s) that create object(s) based on an enum value."

"For example:"

Example
public enum PersonType
{
 UNKNOWN,
 WARRIOR,
 MAG,
 TROLL,
 ELF,
}

public class PersonFactory
{
 public static GamePerson createPerson(PersonType personType)
 {
  switch(personType)
  {
   WARRIOR:
   return new Warrior();
   MAG:
   return new Mag();
   TROLL:
   return new Troll();
   ELF:
   return new Elf();
   default:
   throw new GameException();
  }
 }
}
How it is called
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"In other words, we created a special class to manage object creation?"

"Yep."

"So what advantages does this provide?"

"First, in this class, the objects can be initialized with the necessary data."

"Second, you can pass the needed enum value between methods as much as you like in order to ultimately create the desired object."

"Third, the number of enum fields does not have to match the number of classes. There may be many types of characters, but few classes."

"For example, a Mag & Warrior might use one class (Human), but with different strength and magic properties (constructor arguments)."

"Here's what it might look like (for clarity, I've also added dark elves):"

Example
public enum PersonType
{
 UNKNOWN,
 WARRIOR,
 MAG,
 TROLL,
 ELF,
 DARK_ELF
}

public class PersonFactory
{
 public static GamePerson createPerson(PersonType personType)
 {
  switch(personType)
  {
   WARRIOR:
   return new Human(10, 0); // Strength, magic
   MAG:
   return new Human(0, 10); // Strength, magic
   TROLL:
   OGR:
   return new Troll();
   ELF:
   return new Elf(true); // true – good, false – evil
   DARK_ELF:
   return new Elf(false); // true – good, false – evil
   default:
   throw new GameException();
  }
 }
}
How it is called
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"In the example above, we used just three classes to create six different types of objects. This is very convenient. Moreover, we have all this logic concentrated in one class and one method."

"Now suppose we decide to create a separate class for Ogre — we simply change a couple of lines of code here, and not half the application."

"I agree. That's a good solution."

"And that's what I'm talking about: design patterns are collections of good solutions."

"I also wish I knew where to use them…"

"Yep. I agree, you won't understand right away. Still, it's better to know and not be able to do than to not know and not be able to do. Here's another useful link for you about this pattern: Factory Pattern"

"Oh, thanks."

"Abstract factory pattern."

"Sometimes when you have a lot of objects, the idea of creating a factory for factories suggests itself. Such a factory is called an abstract factory."

"Where would this be needed?!"

"Suppose you have several groups of identical objects. This is easier to show with an example."

"Now, let's say you have three races in your game: humans, elves, and demons. And for balance, each race has warriors, archers, and mages. A player can only create objects belonging to the race he or she is playing for in the game. Here's how it would look in code:"

Declaration of soldier classes
class Warrior
{
}
class Archer
{
}
class Mag
{
}
Humans
class HumanWarrior extends Warrior
{
}

class HumanArcher extends Archer
{
}

class HumanMag extends Mag
{
}
Elves
class ElfWarrior extends Warrior
{
}

class ElfArcher extends Archer
{
}

class ElfMag extends Mag
{
}
Demons
class DaemonWarrior extends Warrior
{
}

class DaemonArcher extends Archer
{
}

class DaemonMag extends Mag
{
}

And now let's create the races, or we could also call them armies.

Armies
abstract class Army
{
 public Warrior createWarrior();
 public Archer createArcher();
 public Mag createMag();
}
Human army
class HumanArmy extends Army
{
 public Warrior createWarrior()
 {
  return new HumanWarrior();
 }
 public Archer createArcher()
 {
  return new HumanArcher();
 }
 public Mag createMag()
 {
  return new HumanMag();
 }
}
Elf army
class ElfArmy extends Army
{
 public Warrior createWarrior()
 {
  return new ElfWarrior();
 }
 public Archer createArcher()
 {
  return new ElfArcher();
 }
 public Mag createMag()
 {
  return new ElfMag();
 }
}
Demon army
class DaemonArmy extends Army
{
 public Warrior createWarrior()
 {
  return new DaemonWarrior();
 }
 public Archer createArcher()
 {
  return new DaemonArcher();
 }
 public Mag createMag()
 {
  return new DaemonMag();
 }
}

"But how do you use this?"

"You can use the Army, Warrior, Archer, and Mage classes anywhere in the program, and to create the necessary objects — simply pass an object of the desired Army subclass."

"For example:"

Example
Army humans = new HumanArmy();
Army daemons = new DaemonArmy();

Army winner = FightSimulator.simulate(humans, daemons);

"In the example above, we have a class that simulates battles between different races (armies). You just need to pass two Army objects. The class itself uses them to creates various troops and conducts virtual battles between them in order to identify the victor."

"I see. Thanks. A really interesting approach."

"A good solution, regardless of what you say."

"Yep."

"Here is another good link on the topic: Abstract factory pattern"

Comments (3)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Andrei Level 41
23 July 2021
I wonder how will the day when I am going to use these patterns look ...
MaGaby2280 Level 41, Guatemala City, Guatemala
8 July 2021
"Our topic today won't merely be interesting — it will be downright epic."... and it certainly was
Tom Level 41, San Jose, Sweden
29 May 2021
very informative!!