"¡Hola, amigo!"

"¡Hola, Bilaabo!"

"Nuestro tema de hoy no será simplemente interesante, será francamente épico".

“Hoy les voy a contar qué son los patrones de diseño ” .

"¡Genial! He oído hablar mucho de ellos. ¡No puedo esperar!"

"Los programadores experimentados tienen que escribir muchas clases. Pero la parte más difícil de este trabajo es decidir qué clases crear y cómo dividir el trabajo entre ellas".

"Cuanto más resolvían esas preguntas, más se daban cuenta de que algunas soluciones son buenas, mientras que otras son malas".

"Las malas soluciones suelen crear más problemas de los que resuelven. Se extienden mal, crean muchas limitaciones innecesarias, etc. Y las buenas soluciones son todo lo contrario".

"¿Hay alguna analogía que puedas hacer?"

"Digamos que estás construyendo una casa. Y estás pensando de qué estará hecha. Decides que necesitas paredes, un piso y un techo. Como resultado, construyes una casa con un techo plano y sin cimientos. Tal casa se agrietará y el techo tendrá goteras. Esta es una mala solución".

"Por el contrario, una casa que consta de cimientos, paredes y un techo a dos aguas sería una buena solución. Las fuertes nevadas no presentan ningún problema, ya que la nieve se deslizará del techo. Y los suelos cambiantes no son nada que temer, porque los cimientos asegurarán estabilidad. Diríamos que tal solución es buena".

"Ya veo. Gracias."

"Está bien. Entonces continuaré".

"Con el tiempo, las buenas soluciones se conocieron como patrones de diseño, mientras que las malas se llamaron antipatrones".

"Un patrón de diseño es como una respuesta a una pregunta. Es difícil de entender si nunca has escuchado la pregunta".

" La primera categoría de patrones son los patrones de creación. Dichos patrones describen buenas soluciones relacionadas con la creación de objetos".

"¿Qué tiene de complicado crear objetos?"

"Da la casualidad de que eso es justo lo que vamos a explorar ahora".

patrón único.

Patrones de diseño: singleton, fábrica, método de fábrica, fábrica abstracta - 1

"A menudo, en los programas, solo puede existir una única copia de algunos objetos. Por ejemplo, la consola, el registrador, el recolector de basura, etc."

" Mala solución: no cree ningún objeto, solo cree una clase cuyos métodos sean todos estáticos".

" Buena solución:  crear un único objeto y almacenarlo en una variable estática. Impedir la creación de un segundo objeto de la clase. Por ejemplo:"

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

  return instance;
 }

 private Sun()
 {
 }
}
como se llama
Sun sun = Sun.getInstance();

"Es sencillo."

"Primero, hacemos que el constructor sea privado. Ahora solo se puede llamar desde dentro de nuestra clase. Hemos bloqueado la creación de objetos Sun en todas partes, excepto dentro de los métodos de la clase Sun".

"En segundo lugar, un objeto de esta clase solo se puede obtener llamando al método getInstance(). No solo es el único método que puede crear un objeto Sun, sino que también garantiza que solo haya uno de esos objetos".

"Veo."

"Cuando piensas, 'Ahora, ¿cómo exactamente haría eso?', un patrón dice, 'puedes probar esto, esta es una de las mejores soluciones'".

"Gracias. Ahora las cosas empiezan a aclararse".

"También puedes leer sobre este patrón  aquí ".

Patrón de fábrica.

Patrones de diseño: singleton, fábrica, método de fábrica, fábrica abstracta - 2

"Aquí hay una situación que los programadores enfrentan muy a menudo. Tienes una clase base y muchas subclases. Por ejemplo, una clase de personaje de juego (GamePerson) y clases para todos los demás personajes que la heredan".

"Digamos que tienes las siguientes clases:"

Ejemplo
abstract class GamePerson
{
}

class Warrior extends GamePerson
{
}

class Mag extends GamePerson
{
}

class Troll extends GamePerson
{
}

class Elf extends GamePerson
{
}

"La pregunta es cómo gestionamos de manera flexible y conveniente la creación de objetos de estas clases".

"Si este problema te parece exagerado, imagina que en el juego necesitas crear docenas de espadas y escudos, cientos de hechizos mágicos y miles de monstruos. No puedes prescindir de un enfoque conveniente para la creación de objetos aquí. "

"El patrón Factory ofrece una buena solución".

"Primero, necesitamos crear una enumeración cuyos valores correspondan a las distintas clases".

"En segundo lugar, cree una clase Factory especial que tenga métodos estáticos que creen objetos en función de un valor de enumeración".

"Por ejemplo:"

Ejemplo
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();
  }
 }
}
como se llama
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"En otras palabras, ¿creamos una clase especial para administrar la creación de objetos?"

"Sí."

"Entonces, ¿qué ventajas proporciona esto?"

"Primero, en esta clase, los objetos se pueden inicializar con los datos necesarios".

"En segundo lugar, puede pasar el valor de enumeración necesario entre los métodos tanto como desee para finalmente crear el objeto deseado".

"Tercero, la cantidad de campos de enumeración no tiene que coincidir con la cantidad de clases. Puede haber muchos tipos de caracteres, pero pocas clases".

"Por ejemplo, un Mag & Warrior podría usar una clase (humano), pero con diferente fuerza y ​​propiedades mágicas (argumentos de constructor)".

"Esto es lo que podría parecer (para mayor claridad, también he añadido elfos oscuros):"

Ejemplo
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();
  }
 }
}
como se llama
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"En el ejemplo anterior, usamos solo tres clases para crear seis tipos diferentes de objetos. Esto es muy conveniente. Además, tenemos toda esta lógica concentrada en una clase y un método".

"Ahora supongamos que decidimos crear una clase separada para Ogre: simplemente cambiamos un par de líneas de código aquí, y no la mitad de la aplicación".

"Estoy de acuerdo. Esa es una buena solución".

"Y de eso es de lo que estoy hablando: los patrones de diseño son colecciones de buenas soluciones".

"También desearía saber dónde usarlos..."

"Sí. Estoy de acuerdo, no lo entenderás de inmediato. Aún así, es mejor saber y no poder hacer que no saber y no poder hacer. Aquí hay otro enlace útil para ti sobre este patrón: Patrón de fábrica "

"Oh gracias."

" Patrón abstracto de fábrica ".

"A veces, cuando tienes muchos objetos, surge la idea de crear una fábrica para fábricas. Tal fábrica se llama fábrica abstracta ".

"¡¿Dónde se necesitaría esto?!"

"Suponga que tiene varios grupos de objetos idénticos. Esto es más fácil de mostrar con un ejemplo".

"Ahora, digamos que tienes tres razas en tu juego: humanos, elfos y demonios. Y para mantener el equilibrio, cada raza tiene guerreros, arqueros y magos. Un jugador solo puede crear objetos que pertenezcan a la raza para la que está jugando. en el juego. Así es como se vería en el código:"

Declaración de clases de soldados
class Warrior
{
}
class Archer
{
}
class Mag
{
}
humanos
class HumanWarrior extends Warrior
{
}

class HumanArcher extends Archer
{
}

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

class ElfArcher extends Archer
{
}

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

class DaemonArcher extends Archer
{
}

class DaemonMag extends Mag
{
}

Y ahora vamos a crear las razas, o también podríamos llamarlas ejércitos.

ejércitos
abstract class Army
{
 public Warrior createWarrior();
 public Archer createArcher();
 public Mag createMag();
}
ejercito humano
class HumanArmy extends Army
{
 public Warrior createWarrior()
 {
  return new HumanWarrior();
 }
 public Archer createArcher()
 {
  return new HumanArcher();
 }
 public Mag createMag()
 {
  return new HumanMag();
 }
}
ejército de elfos
class ElfArmy extends Army
{
 public Warrior createWarrior()
 {
  return new ElfWarrior();
 }
 public Archer createArcher()
 {
  return new ElfArcher();
 }
 public Mag createMag()
 {
  return new ElfMag();
 }
}
ejercito demoníaco
class DaemonArmy extends Army
{
 public Warrior createWarrior()
 {
  return new DaemonWarrior();
 }
 public Archer createArcher()
 {
  return new DaemonArcher();
 }
 public Mag createMag()
 {
  return new DaemonMag();
 }
}

"¿Pero cómo usas esto?"

"Puede usar las clases Army, Warrior, Archer y Mage en cualquier parte del programa, y ​​para crear los objetos necesarios, simplemente pase un objeto de la subclase Army deseada".

"Por ejemplo:"

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

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

"En el ejemplo anterior, tenemos una clase que simula batallas entre diferentes razas (ejércitos). Solo necesitas pasar dos objetos Army. La clase misma los usa para crear varias tropas y lleva a cabo batallas virtuales entre ellas para identificar al vencedor. ."

"Ya veo. Gracias. Un enfoque realmente interesante".

"Una buena solución, independientemente de lo que digas".

"Sí."

"Aquí hay otro buen enlace sobre el tema:  Patrón de fábrica abstracto "