— Привіт, Аміго!

— Привіт, Білаабо!

— Сьогодні у нас буде не просто цікава, а епічна тема.

Сьогодні я розповім тобі, що таке шаблони проектування (design patterns).

— Круто. Багато чув про них. Чекаю з нетерпінням.

— Досвідченим програмістам доводиться писати дуже багато класів. Але найскладніша частина цієї роботи – це вирішувати, які класи мають бути і як розподілити роботу між ними. .

Невдалі рішення зазвичай створюють проблем більше, ніж вирішують. Вони погано розширюються, створюють багато зайвих обмежень тощо. А вдалі рішення – навпаки.

— А можна якусь аналогію?

— Допустимо, ти будуєш будинок. І думаєш — з чого він повинен складатися. Ти вирішив, що тобі потрібні стіни, підлога та стеля. В результаті ти збудував будинок без фундаменту і з рівним дахом. Такий будинок тріскатиметься, а дах – протікатиме. Це – невдале рішення.

І навпаки: будинок, що складається з фундаменту, стін та двосхилий даху буде вдалим рішенням. Йому не страшні ні великі снігопади (сніг скочуватиметься з даху), ні зрушення ґрунту – фундамент забезпечуватиме стабільність. Таке рішення ми назвемо вдалим.

— Зрозуміло. Дякую.

— Ок. Тоді я продовжу.

Згодом збірку вдалих рішень оголосили «патернами (шаблонами) проектування», а збірка невдалих – антипаттернами.

Сам шаблон проектування — це як би відповідь на запитання. Його складно зрозуміти, якщо не чув самого питання.

Перша категорія патернів – це патерни, що породжують. Такі патерни описують вдалі рішення, пов'язані зі створенням об'єктів .

— А що такого складного у створенні об'єктів?

— А ось якраз зараз ми це і розберемо. Патерн Singleton - Сінглетон, Одинак. -4e9d-a5a6-c0acf53b163e/original.jpeg' target='_blank'>

програмі деякі об'єкти можуть існувати лише в єдиному екземплярі. Наприклад, консоль, логер, збирач сміття тощо.

Невдале рішення: відмовитися від створення об'єктів, просто створити клас у якого всі методи статичні .

Вдале рішення: створити єдиний об'єкт класу і зберігати його в статичній змінній. Заборонити створення другого об'єкта цього. Приклад:

Приклад
class Sun { private static Sun instance; public static Sun getInstance() { if (instance == null) instance = new Sun(); return instance; } private Sun() { } }
Як викликати
Sun sun = Sun.getInstance();

Все просто.

По-перше, ми зробили конструктор private. Тепер його можна викликати лише зсередини нашого класу. Ми заборонили створення об'єкта Sun скрізь крім методів класу Sun.

По-друге, отримати об'єкт цього класу можна лише викликавши метод getInstance(). Це не тільки єдиний метод, який може створювати об'єкти класу Sun, але ще й стежить, щоб такий об'єкт був єдиним.

— Ясно.

— Коли людина думає – як саме це зробити? Паттерн каже – можеш спробувати так – це одне з вдалих рішень.

— Дякую. Тепер щось починає прояснюватись.

— Також про цей патерн можна прочитати тут.

Паттерн Factory – Фекторі, Фабрика .

Паттерни проектування: Singleton, Factory, FactoryMethod, AbstractFactory - 2

Дуже часто програмісти стикаються ось з якою ситуацією. У тебе є певний базовий клас та багато підкласів. Наприклад – персонаж гри – GamePerson та класи решти персонажів гри, успадковані від нього.

Припустимо, у тебе є такі класи:

Приклад
abstract class GamePerson { } class Warrior extends GamePerson { } class Mag extends GamePerson { } class Troll extends GamePerson { } class Elv extends GamePerson { }

Питання полягає в тому, як гнучко та зручно керувати створенням об'єктів цих класів.

Ось яке «вдале рішення» пропонує патерн Фабрика (Factory).

Во- перше, треба завести enum, значення якого будуть відповідати різним класам. об'єкта(ів) залежно від enum’а.

Приклад:

Приклад
public enum PersonType { UNKNOWN, WARRIOR, MAG, TROLL, ELV, } public class PersonFactory { public static GamePerson createPerson(PersonType personType) { switch(personType ) { WARRIOR: return new Warrior(); MAG: return new Mag(); TROLL: return new Troll(); ELV: return new Elv(); default: throw new GameException(); } } }
Як викликати
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

— Тобто. ми створили спеціальний клас для керування створенням об'єктів?

— Ага.

— А які переваги це дає?

— По-перше, там ці об'єкти можна ініціалізувати потрібними даними.

По-друге, між методами можна скільки завгодно передавати потрібний enum, щоб зрештою по ньому створили правильний об'єкт.

В третє, кількість значень enum не обов'язково має співпадати з кількістю класів. Типів персонажів може бути багато, а класів – мало.

Наприклад, для Mag & Warrior можна використовувати один клас – Human, але з різними налаштуваннями сили та магії (параметрами конструктора).

Ось як це може виглядати (для наочності додав ще темних ельфів):

< div class="code-heading">Приклад
public enum PersonType { UNKNOWN, WARRIOR, MAG, TROLL, ELV, DARK_ELV } public class PersonFactory { public static GamePerson createPerson(PersonType personType) { switch(personType) { WARRIOR: return new Human(10, 0); //сила, магія MAG: return new Human(0, 10); //сила, магія TROLL: OGR: return new Troll(); ELV: return new Elv (true); // true - добрий, false - злий DARK_ELV: return new Elv (false); // true - добрий, false - злий default: throw new GameException (); } } }
Як викликати
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

У прикладі вище ми використовували всього три класи, щоб створювати об'єкти шести різних типів. Це дуже зручно. Більше того, у нас вся ця логіка зосереджена в одному класі та в одному методі.

Якщо ми раптом вирішимо створити окремий клас для Огра, ми просто поміняємо тут пару рядків коду, а не перелопачуватимемо половину додатку.< /p> — Згоден. Вдале рішення.

— А я тобі про що говорю, шаблони проектування – це збірки вдалих рішень.

— Знати б ще, де їх застосовувати…

— Ага. Відразу не розберешся, згоден. Але все одно краще знати і не вміти, ніж не знати і не вміти. Ось тобі ще корисне посилання на цей патерн: Паттерн Factory

— О, дякую.

— Паттерн Abstract Factory – Абстрактна Фабрика.

Іноді, коли об'єктів дуже багато, напрошується створення фабрики фабрик. Таку фабрику прийнято називати Абстрактною Фабрикою.

— Це де таке може знадобитися?

— Ну, наприклад, ти маєш кілька груп ідентичних об'єктів. Це легше показати на прикладі.

Дивися, припустимо, у тебе в грі є три раси – люди, ельфи та демони. І кожна раса для балансу має воїна, лучника і мага. Залежно від цього, який бік грає людина, може створювати лише об'єкти своєї раси. Ось як це могло б виглядати в коді:

Оголошення класів військ
class Warrior { } class Archer { } class Mag { }
Люди
class HumanWarrior extends Warrior { } class HumanArcher extends Archer { } class HumanMag extends Mag { }
Ельфи
class ElvWarrior extends Warrior { } class ElvArcher extends Archer { } class ElvMag extends Mag { }
Демони
class DaemonWarrior extends Warrior { } class DaemonArcher extends Archer { } class DaemonMag extends Mag { }

А тепер створимо раси, ну чи можна ще назвати їх арміями.

Армії
< pre-class="line-numbers language-java" data-line="" data-start="">abstract class Army { public Warrior createWarrior(); public Archer createArcher(); public Mag createMag(); }
Армія людей
 class HumanArmy extends Army { public Warrior createWarrior() { return new HumanWarrior(); } public Archer createArcher() { return new HumanArcher(); } public Mag createMag() { return new HumanMag(); } }
Армія ельфів
class ElvArmy extends Army { public Warrior createWarrior() { return new ElvWarrior(); } public Archer createArcher() { return new ElvArcher(); } public Mag createMag() { return new ElvMag(); } }
Армія демонів
class DaemonArmy extends Army { public Warrior createWarrior() { return new DaemonWarrior(); } public Archer createArcher() { return new DaemonArcher(); } public Mag createMag() { return new DaemonMag(); } }

— Як це використовувати?

— Можна скрізь у програмі використовувати класи Army, Warrior, Archer, Mag, а для створення потрібних об'єктів – просто передавати об'єкт потрібного класу-спадкоємця Army.

Приклад:

Приклад
Army humans = new HumanArmy(); Army daemons = новий DaemonArmy(); Army winner = FightSimulator.simulate(humans, daemons);

У прикладі вище у нас є клас, який симулює бої між різними расами (арміями), і йому потрібно просто передати два об'єкти Army. З їх допомогою він сам створює різні війська та проводить віртуальні бої між ними, з метою виявити переможця.

— Зрозуміло. Дякую.Дійсно цікавий підхід.

Вдале рішення, що не кажи.

— Ага.

Ось ще хороше посилання на цю тему: Паттерн Abstract Factory< /p>