"Bună, Amigo!"

— Bună, Bilaabo!

„Subiectul nostru de astăzi nu va fi doar interesant, ci va fi de-a dreptul epic”.

„Astăzi am să vă spun ce sunt modelele de design.

"Cool! Am auzit multe despre ei. Abia astept!"

„Programatorii cu experiență trebuie să scrie o mulțime de cursuri. Dar cea mai dificilă parte a acestui job este să decidă ce clase să creeze și cum să împarți munca între ele.”

„Cu cât au rezolvat mai multe astfel de întrebări, cu atât au realizat că unele soluții sunt bune, în timp ce altele sunt rele”.

"Soluțiile proaste creează, de obicei, mai multe probleme decât rezolvă. Se extind prost, creează multe limitări inutile etc. Și soluțiile bune sunt opusul."

— Există vreo analogie pe care o poți face?

„Să presupunem că construiești o casă. Și te gândești la ce va fi făcută. Decizi că ai nevoie de pereți, podea și tavan. Drept urmare, construiești o casă cu acoperiș plat și fără fundație. O astfel de casă se va crăpa și acoperișul se va scurge. Aceasta este o soluție proastă."

„Dimpotrivă, o casă formată dintr-o fundație, pereți și un acoperiș în fronton ar fi o soluție bună. Căderea abundentă de zăpadă nu prezintă nicio problemă, deoarece zăpada va aluneca de pe acoperiș. Iar solurile în mișcare nu trebuie să vă temeți, deoarece fundația va asigura stabilitate. Am numi o astfel de soluție bună."

— Înțeleg. Mulțumesc.

"OK. Atunci voi continua."

„În timp, soluțiile bune au ajuns să fie cunoscute sub denumirea de modele de design, în timp ce cele proaste au fost numite anti-modeluri”.

„Un model de design este ca un răspuns la o întrebare. Este greu de înțeles dacă nu ai auzit niciodată întrebarea.”

Prima categorie de modele este modelele de creație. Astfel de modele descriu soluții bune legate de crearea de obiecte.”

„Ce este atât de complicat în a crea obiecte?”

„Așa cum se întâmplă, asta este exact ceea ce vom explora acum.”

Model singleton.

Modele de design: singleton, fabrică, metoda fabricii, fabrică abstractă - 1

"Adesea, în programe, poate exista doar o singură copie a unor obiecte. De exemplu, consola, loggerul, colectorul de gunoi etc."

Soluție proastă: nu creați niciun obiect – creați doar o clasă ale cărei metode sunt toate statice.”

" Soluție bună:  creați un singur obiect și stocați-l într-o variabilă statică. Preveniți crearea unui al doilea obiect al clasei. De exemplu:"

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

  return instance;
 }

 private Sun()
 {
 }
}
Cum se numeste
Sun sun = Sun.getInstance();

"E simplu."

„În primul rând, facem constructorul privat. Acum poate fi apelat doar din interiorul clasei noastre. Am blocat crearea de obiecte Sun peste tot, cu excepția metodelor din clasa Sun.”

„În al doilea rând, un obiect din această clasă poate fi obținut doar prin apelarea metodei getInstance(). Nu numai că aceasta este singura metodă care poate crea un obiect Sun, dar asigură și că există un singur astfel de obiect.”

"Înțeleg."

„Când te gândești „Acum, cum aș face exact asta?”, spune un model, „poți încerca asta - aceasta este una dintre cele mai bune soluții.”

"Mulțumesc. Acum lucrurile încep să devină clare."

„De asemenea, puteți citi despre acest model  aici .”

Model de fabrică.

Modele de design: singleton, fabrică, metoda fabricii, fabrică abstractă - 2

"Iată o situație cu care programatorii se confruntă foarte des. Aveți o clasă de bază și multe subclase. De exemplu, o clasă de personaje de joc (GamePerson) și clase pentru toate celelalte personaje care o moștenesc."

„Să presupunem că aveți următoarele clase:”

Exemplu
abstract class GamePerson
{
}

class Warrior extends GamePerson
{
}

class Mag extends GamePerson
{
}

class Troll extends GamePerson
{
}

class Elf extends GamePerson
{
}

„Întrebarea este cum gestionăm în mod flexibil și convenabil crearea de obiecte din aceste clase.”

„Dacă această problemă vi se pare exagerată, imaginați-vă că în joc trebuie să creați zeci de săbii și scuturi, sute de vrăji magice și mii de monștri. Nu vă puteți lipsi de o abordare convenabilă a creării obiectelor aici. "

„ Modelul Factory oferă o soluție bună.”

„În primul rând, trebuie să creăm o enumerare ale cărei valori corespund diferitelor clase.”

„În al doilea rând, creați o clasă specială Factory care are metode statice care creează obiecte pe baza unei valori enumerate.”

"De exemplu:"

Exemplu
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();
  }
 }
}
Cum se numeste
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

„Cu alte cuvinte, am creat o clasă specială pentru a gestiona crearea de obiecte?”

"Da."

„Deci ce avantaje oferă acest lucru?”

„În primul rând, în această clasă, obiectele pot fi inițializate cu datele necesare.”

„În al doilea rând, puteți trece valoarea de enumerare necesară între metode atât cât doriți, pentru a crea în cele din urmă obiectul dorit.”

"În al treilea rând, numărul de câmpuri enumerate nu trebuie să se potrivească cu numărul de clase. Pot exista multe tipuri de caractere, dar puține clase."

„De exemplu, un Mag & Warrior ar putea folosi o singură clasă (Uman), dar cu forță și proprietăți magice diferite (argumente ale constructorului).”

„Iată cum ar putea arăta (pentru claritate, am adăugat și elfi întunecați):”

Exemplu
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();
  }
 }
}
Cum se numeste
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"În exemplul de mai sus, am folosit doar trei clase pentru a crea șase tipuri diferite de obiecte. Acest lucru este foarte convenabil. În plus, avem toată această logică concentrată într-o singură clasă și o singură metodă."

„Acum să presupunem că decidem să creăm o clasă separată pentru Ogre – pur și simplu schimbăm câteva linii de cod aici, și nu jumătate din aplicație.”

"Sunt de acord. E o soluție bună."

„Și despre asta vorbesc: modelele de design sunt colecții de soluții bune”.

„De asemenea, mi-aș dori să știu unde să le folosesc…”

"Da. Sunt de acord, nu vei înțelege imediat. Totuși, este mai bine să știi și să nu poți face decât să nu știi și să nu poți face. Iată un alt link util pentru tine despre acest model: Modelul de fabrică "

"Oh multumesc."

„ Model abstract de fabrică ”.

„Uneori, când ai o mulțime de obiecte, ideea de a crea o fabrică pentru fabrici se sugerează de la sine. O astfel de fabrică se numește o fabrică abstractă .”

"Unde ar fi nevoie de asta?!"

„Să presupunem că aveți mai multe grupuri de obiecte identice. Acest lucru este mai ușor de arătat cu un exemplu.”

„Acum, să presupunem că ai trei rase în jocul tău: oameni, elfi și demoni. Și pentru echilibru, fiecare rasă are războinici, arcași și magi. Un jucător poate crea doar obiecte aparținând rasei pentru care joacă. în joc. Iată cum ar arăta în cod:"

Declarația claselor de soldați
class Warrior
{
}
class Archer
{
}
class Mag
{
}
Oamenii
class HumanWarrior extends Warrior
{
}

class HumanArcher extends Archer
{
}

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

class ElfArcher extends Archer
{
}

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

class DaemonArcher extends Archer
{
}

class DaemonMag extends Mag
{
}

Și acum să creăm cursele, sau le-am putea numi și armate.

Armate
abstract class Army
{
 public Warrior createWarrior();
 public Archer createArcher();
 public Mag createMag();
}
Armata umană
class HumanArmy extends Army
{
 public Warrior createWarrior()
 {
  return new HumanWarrior();
 }
 public Archer createArcher()
 {
  return new HumanArcher();
 }
 public Mag createMag()
 {
  return new HumanMag();
 }
}
Armata de elfi
class ElfArmy extends Army
{
 public Warrior createWarrior()
 {
  return new ElfWarrior();
 }
 public Archer createArcher()
 {
  return new ElfArcher();
 }
 public Mag createMag()
 {
  return new ElfMag();
 }
}
Armata de demoni
class DaemonArmy extends Army
{
 public Warrior createWarrior()
 {
  return new DaemonWarrior();
 }
 public Archer createArcher()
 {
  return new DaemonArcher();
 }
 public Mag createMag()
 {
  return new DaemonMag();
 }
}

— Dar cum folosești asta?

„Puteți folosi clasele Army, Warrior, Archer și Mage oriunde în program și pentru a crea obiectele necesare — pur și simplu treceți un obiect din subclasa Army dorită.”

"De exemplu:"

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

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

„În exemplul de mai sus, avem o clasă care simulează bătălii între diferite rase (armate). Trebuie doar să treci peste două obiecte ale Armatei. Clasa însăși le folosește pentru a crea diverse trupe și conduce bătălii virtuale între ele pentru a identifica învingătorul. ."

— Înțeleg. Mulțumesc. O abordare cu adevărat interesantă.

„O soluție bună, indiferent de ce spui.”

"Da."

„Iată un alt link bun pe această temă:  Model abstract de fabrică