"Ciao, Amico!"

"Ciao Bilaabo!"

"Il nostro argomento di oggi non sarà solo interessante, sarà decisamente epico".

"Oggi ti parlerò di cosa sono i design pattern. "

"Fantastico! Ne ho sentito parlare molto. Non vedo l'ora!"

"I programmatori esperti devono scrivere molte classi. Ma la parte più difficile di questo lavoro è decidere quali classi creare e come suddividere il lavoro tra di esse."

"Più hanno risolto tali domande, più si sono resi conto che alcune soluzioni sono buone, mentre altre sono cattive."

"Le cattive soluzioni di solito creano più problemi di quanti ne risolvano. Si estendono male, creano molte limitazioni inutili, ecc. E le buone soluzioni sono l'opposto."

"C'è qualche analogia che puoi fare?"

"Diciamo che stai costruendo una casa. E stai pensando a come sarà fatta. Decidi di aver bisogno di pareti, pavimento e soffitto. Di conseguenza, costruisci una casa con un tetto piatto e senza fondamenta. Una casa del genere si spezzerà e il tetto perderà. Questa è una pessima soluzione."

"Al contrario, una casa composta da fondamenta, muri e un tetto a due falde sarebbe una buona soluzione. Le abbondanti nevicate non rappresentano un problema, poiché la neve scivolerà dal tetto. E i terreni in movimento non sono nulla da temere, perché le fondamenta garantiranno stabilità. Definiremmo buona una tale soluzione."

"Capisco. Grazie."

"OK. Allora continuerò."

"Col tempo, le buone soluzioni sono diventate note come design pattern, mentre quelle cattive sono state chiamate anti-pattern".

"Un design pattern è come una risposta a una domanda. È difficile da capire se non hai mai sentito la domanda."

" La prima categoria di modelli sono i modelli creazionali. Tali modelli descrivono buone soluzioni relative alla creazione di oggetti."

"Cosa c'è di così complicato nel creare oggetti?"

"Come succede, è proprio quello che esploreremo ora."

Modello singleton.

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

"Spesso nei programmi può esistere solo una singola copia di alcuni oggetti. Ad esempio, la console, il logger, il garbage collector, ecc."

" Cattiva soluzione: non creare alcun oggetto, crea solo una classe i cui metodi sono tutti statici."

" Buona soluzione:  creare un singolo oggetto e memorizzarlo in una variabile statica. Impedire la creazione di un secondo oggetto della classe. Ad esempio:"

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

  return instance;
 }

 private Sun()
 {
 }
}
Come si chiama
Sun sun = Sun.getInstance();

"È semplice."

"In primo luogo, rendiamo privato il costruttore. Ora può essere chiamato solo dall'interno della nostra classe. Abbiamo bloccato la creazione di oggetti Sun ovunque tranne che all'interno dei metodi della classe Sun."

"In secondo luogo, un oggetto di questa classe può essere ottenuto solo chiamando il metodo getInstance(). Non solo questo è l'unico metodo in grado di creare un oggetto Sun, ma garantisce anche che esista un solo oggetto di questo tipo."

"Vedo."

"Quando pensi: «Ora, come lo farei esattamente?», uno schema dice, «puoi provare questo - questa è una delle migliori soluzioni.»"

"Grazie. Ora le cose cominciano a diventare chiare."

"Puoi anche leggere di questo modello  qui ."

Modello di fabbrica.

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

"Ecco una situazione che i programmatori affrontano molto spesso. Hai una classe base e molte sottoclassi. Ad esempio, una classe di personaggi di gioco (GamePerson) e classi per tutti gli altri personaggi che la ereditano."

"Supponiamo che tu abbia le seguenti classi:"

Esempio
abstract class GamePerson
{
}

class Warrior extends GamePerson
{
}

class Mag extends GamePerson
{
}

class Troll extends GamePerson
{
}

class Elf extends GamePerson
{
}

"La domanda è come gestire in modo flessibile e conveniente la creazione di oggetti di queste classi".

"Se questo problema ti sembra inverosimile, immagina che nel gioco devi creare dozzine di spade e scudi, centinaia di incantesimi e migliaia di mostri. Non puoi fare a meno di un approccio conveniente alla creazione di oggetti qui. "

"Il modello Factory offre una buona soluzione."

"Per prima cosa, dobbiamo creare un enum i cui valori corrispondano alle varie classi."

"Secondo, crea una classe Factory speciale che abbia metodi statici che creano oggetti basati su un valore enum."

"Per esempio:"

Esempio
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();
  }
 }
}
Come si chiama
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"In altre parole, abbiamo creato una classe speciale per gestire la creazione di oggetti?"

"Sì."

"Quindi quali vantaggi offre questo?"

"In primo luogo, in questa classe, gli oggetti possono essere inizializzati con i dati necessari."

"In secondo luogo, puoi passare il valore enum necessario tra i metodi quanto vuoi per creare alla fine l'oggetto desiderato."

"In terzo luogo, il numero di campi enum non deve corrispondere al numero di classi. Potrebbero esserci molti tipi di caratteri, ma poche classi."

"Ad esempio, un Mag & Warrior potrebbe usare una classe (Umano), ma con forza e proprietà magiche diverse (argomenti del costruttore)."

"Ecco come potrebbe apparire (per chiarezza, ho anche aggiunto gli elfi oscuri):"

Esempio
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();
  }
 }
}
Come si chiama
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"Nell'esempio precedente, abbiamo utilizzato solo tre classi per creare sei diversi tipi di oggetti. Questo è molto conveniente. Inoltre, abbiamo tutta questa logica concentrata in una classe e un metodo."

"Ora supponiamo di decidere di creare una classe separata per Ogre: qui cambiamo semplicemente un paio di righe di codice e non metà dell'applicazione."

"Sono d'accordo. Questa è una buona soluzione."

"Ed è di questo che sto parlando: i modelli di design sono raccolte di buone soluzioni."

"Vorrei anche sapere dove usarli..."

"Sì. Sono d'accordo, non capirai subito. Tuttavia, è meglio sapere e non essere in grado di fare piuttosto che non sapere e non essere in grado di fare. Ecco un altro link utile per te su questo pattern: Factory Pattern "

"Oh, grazie."

" Modello di fabbrica astratto ."

"A volte, quando hai molti oggetti, l'idea di creare una fabbrica per le fabbriche viene da sola. Una fabbrica del genere si chiama fabbrica astratta ".

"Dove sarebbe necessario questo?!"

"Supponi di avere diversi gruppi di oggetti identici. Questo è più facile da mostrare con un esempio."

"Ora, supponiamo che tu abbia tre razze nel tuo gioco: umani, elfi e demoni. E per equilibrio, ogni razza ha guerrieri, arcieri e maghi. Un giocatore può creare solo oggetti appartenenti alla razza per cui sta giocando nel gioco. Ecco come apparirebbe nel codice:"

Dichiarazione delle classi di soldati
class Warrior
{
}
class Archer
{
}
class Mag
{
}
Umani
class HumanWarrior extends Warrior
{
}

class HumanArcher extends Archer
{
}

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

class ElfArcher extends Archer
{
}

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

class DaemonArcher extends Archer
{
}

class DaemonMag extends Mag
{
}

E ora creiamo le razze, o potremmo anche chiamarle eserciti.

Eserciti
abstract class Army
{
 public Warrior createWarrior();
 public Archer createArcher();
 public Mag createMag();
}
Esercito umano
class HumanArmy extends Army
{
 public Warrior createWarrior()
 {
  return new HumanWarrior();
 }
 public Archer createArcher()
 {
  return new HumanArcher();
 }
 public Mag createMag()
 {
  return new HumanMag();
 }
}
Esercito di Elfi
class ElfArmy extends Army
{
 public Warrior createWarrior()
 {
  return new ElfWarrior();
 }
 public Archer createArcher()
 {
  return new ElfArcher();
 }
 public Mag createMag()
 {
  return new ElfMag();
 }
}
Esercito di demoni
class DaemonArmy extends Army
{
 public Warrior createWarrior()
 {
  return new DaemonWarrior();
 }
 public Archer createArcher()
 {
  return new DaemonArcher();
 }
 public Mag createMag()
 {
  return new DaemonMag();
 }
}

"Ma come si usa questo?"

"Puoi utilizzare le classi Esercito, Guerriero, Arciere e Mago ovunque nel programma e per creare gli oggetti necessari: passa semplicemente un oggetto della sottoclasse dell'Esercito desiderata."

"Per esempio:"

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

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

"Nell'esempio sopra, abbiamo una classe che simula battaglie tra diverse razze (eserciti). Devi solo passare due oggetti dell'esercito. La classe stessa li usa per creare varie truppe e conduce battaglie virtuali tra di loro per identificare il vincitore ."

"Capisco. Grazie. Un approccio davvero interessante."

"Una buona soluzione, indipendentemente da quello che dici."

"Sì."

"Ecco un altro buon collegamento sull'argomento:  Abstract pattern di fabbrica "