Factory
Factory е модел на проектиране на обекти, който осигурява интерфейс за създаване на обекти в базов клас, но позволява на подкласовете да променят вида на обектите, които ще бъдат създадени.
Проблема
Представете си, че създавате приложение за управление на логистиката. Първата версия на приложението ви може да се справи само с транспортирането с камиони, така че основната част от кода ви бъде в този клас.
След известно време приложението ви става доста популярно. Всеки ден получавате десетки заявки от фирми за морски превози за включване на морска логистика в приложението.
Понастоящем по-голямата част от кода е съсредоточена в един клас. Добавянето в приложението би изисквало извършване на промени в цялата програма. Освен това, ако по-късно решите да добавите друг вид транспорт към приложението, вероятно ще трябва да направите всички тези промени отново.
В резултат на това ще направите доста лош код, пълен с условия, които превключват поведението на приложението в зависимост от класа на транспортните обекти.
Решението
Factory предполага, че замествате преките повиквания за изграждане на обекти (с помощта на оператора) с повиквания към специален фабричен метод. Обектите все още се създават чрез конструктор, но той се извиква вътре в фабриката. Обектите, връщани по фабриката, често се обозначават като продукти.
На пръв поглед тази промяна може да изглежда безсмислена: току-що преместихме конструкторното обаждане от една част на програмата в друга. Помислете обаче за това: сега можете да се премести фабриката в подклас и да промените класовете продукти, които се създават по метода.
Има леко ограничение обаче: подкласовете може да връщат различни видове продукти само ако тези продукти имат общ базов клас или интерфейс. Също така, фабричният метод в базовия клас трябва да има своя тип връщане, обявен като този интерфейс.
Например, както и класовете трябва да внедрят интерфейса, който декларира метод за създаване. Всеки клас прилага този метод по различен начин: камионите доставят товари по суша, корабите доставят товари по море. Фабриката връща предметите на товарния автомобил, докато фабричният метод в класа за кораби връща корабите.
Кодът, който използва фабриката (често наричан клиентски код), не вижда разлика между действителните продукти, върнати от различни подкласове. Клиентът третира всички продукти като абстрактни. Клиентът знае, че всички транспортни обекти се предполага, че имат метода, но точно как работи не е важно за клиента.
Използване
1. Фабриката се използва, когато не знаете предварително точните типове и зависимости на обектите, с които трябва да работи кодът ви.
2. Фабриката се използва, когато искате да предоставите на потребителите на вашата библиотека или рамка начин за разширяване на вътрешните му компоненти.
3. Фабриката се използва, когато искате да запишете системни ресурси, като използвате повторно съществуващите обекти, вместо да ги възстановите всеки път.
Имплементация
- Накарайте всички продукти да следват един и същ интерфейс. Този интерфейс трябва да декларира методи, които са смислени във всеки продукт.
- Фабриката служи за създаване на обекти. Типът връщане на метода трябва да съответства на общия продуктов интерфейс.
- В кода на създателя намерете всички препратки към конструктори на продукти. Един по един, заменете ги с повиквания към фабрика, като същевременно извлечете кода за създаване на продукта във фабриката. В този момент кодът на фабриката може да изглежда доста грозен. Той може да има голям оператор, който избира кой продуктов клас да инстанцира.
- Сега, създайте набор от подкласове създатели за всеки тип продукт, посочен в фабриката. Заменете метода за създаване в подкласовете и извлечете подходящите битове на строителния код от базовия метод.
- Ако има твърде много типове продукти и няма смисъл да създавате подкласове за всички тях, можете да използвате повторно параметъра за управление от базовия клас в подкласове.
- Ако след всички екстракции методът на базовата фабрика е станал празен, можете да го направите абстрактен. Ако е останало нещо, можете да го направите поведение по подразбиране на метода.
Положителни страни | Отрицателни страни |
---|---|
Избягвате плътното съвръзване между създателя и бетонните продукти. | Кодът може да стане по-сложен, тъй като трябва да се въведат много нови подкласове за прилагане на модела. Най-добрият сценарий е, когато въвеждате шаблона в съществуваща йерархия от класове създатели. |
Единен отговорен принцип. Можете да преместите кода за създаване на продукта на едно място в програмата, което прави кода по-лесен за поддръжка. | |
Принцип на отворено/затворено. Можете да въвеждате нови видове продукти в програмата, без да нарушавате съществуващия клиентски код. |
Пример
Създайте приложение за изпращане на нотификации по SMS, Email и Push (изкачащи)
Нотификациите ще се изпращат с метод notifyUser за да мопже различните доставчици на съобщения да приложарт това поведение е нужно да имат обща интерфейс.
public interface Notification {
void notifyUser();
}
Интерфейса ще се наследи от класовете доставящи нотификации
public class SMSNotification implements Notification {
@Override
public void notifyUser()
{
System.out.println("Sending an SMS notification");
}
}
public class EmailNotification implements Notification {
@Override
public void notifyUser()
{
System.out.println("Sending an e-mail notification");
}
}
public class PushNotification implements Notification {
@Override
public void notifyUser()
{
System.out.println("Sending a push notification");
}
}
Factory
Създаването на обектите от конкретните класове ще минава през фактори клас
public class SMSNotificationFactory {
public SMSNotification create() {
return new SMSNotification();
}
}
public class EmailNotificationFactory {
public EmailNotification create() {
return new EmailNotification();
}
}
public class PushNotificationFactory {
public PushNotification create() {
return new PushNotification();
}
}
Самото създаване на обекти минава през фактори метода по следния начин