В большинстве источников рассказывается какие есть паттерны и как их реализовывать, но после прочтения остается неясным когда и как их применять, что в итоге приводит к
Зачем нужны паттерны?
Согласно одному из определений «это лишь пример решения задачи, который можно использовать в различных ситуациях.» Неясно только какое решение каких задач и в каких ситуациях его использовать
На мой взгляд, главная задача шаблонов проектирования — это определение и выделение ответственности. Для порождающих паттернов — это создание объектов. Таким образом, независимо от того, какая поставлена задача, если необходимо при ее решении создавать объекты, то можно применять порождающие паттерны.
Фабричный метод, фабрики, абстрактные фабрики
Самое четкое и понятное определение, которое я нашел, описано в книге Кириевски:
- Creation method (порождающий метод) — статический или нестатический метод, который создает и возвращает объекты.
- Factory method — это нестатический метод, возвращающий тип базового класса или интерфейса и реализованный в иерархии классов для обеспечения возможности полиморфного создания.
- Factory (Фабрика) — это класс, реализующий один или несколько методов создания.
- Abstract Factory (абстрактная фабрика) — это интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.
Все. Никаких диаграм и запутанных примеров.
- Если нам нужно создавать объект, то используем порождающий метод.
- Если нам нужно создавать другие объекты (реализующие тот же интерфейс или унаследованные от базового класса) или те же объекты, но другим способом в дочерних классах, то делаем метод виртуальным и переопределяем в дочерних классах (реализуем фабричный метод).
- Если нам нужно использовать порождающий метод из нескольких мест, то выделяем его в отдельный класс (фабрику).
- Если нам нужна возможность создавать объекты разными способами, то выделяем из фабрики интерфейс (абстрактная фабрика) и используем его.
Получилось немного утрированно. Есть более хитрые случаи использования этих паттернов и способы их реализации, но в большинстве случаев, особенно начинающим программистам, можно действовать по этой схеме.
Хочу еще отметить, что я видел как абстрактную фабрику иногда называют фабрикой фабрик, что неправильно. Абстрактная фабрика позволяет использовать интерфейс и не привязываться к конкретной реализации, а не создавать объект создающий объекты.
Порождающий метод
Все-таки на примерах понятнее.
Допустим, наше приложение позволяет генерировать сообщения, причем сообщения могут быть двух типов: текст и картинка с подписью. Для того, чтобы создавать сообщения этих двух типов, мы можем использовать два конструктора.
public class Message
{
public String Text{get; set;}
public Image Picture{get; set;}
public Message(String text)
{
Text = text;
}
public Message(String text, Image image)
{
Text = text;
Image = image;
}
}
У этого подхода есть масса недостатков. Из названия конструкторов непонятно чем они отличаются. Есть ли разница вызывать конструктор без картинки или передать null вместо картинки? Необходима дополнительная документация. Если мы захотим поменять поведение конструктора (например, добавить какой-нибудь префикс в сообщение) нам нужно менять либо все вызовы этого конструктора либо вводить дополнительные параметры, что усложняет понимание кода и повышает вероятность ошибок.
С другой стороны, у нас есть явная ответственность — «создание новых сообщений разных типов». Выделяем ее в отдельные методы.
public class Message
{
public String Text{get; set;}
public Image Picture{get; set;}
public Message(String text, Image image)
{
Text = text;
Image = image;
}
public static Message CreateTextMessage(String text)
{
return new Message(text, null);
}
public static Message CreateImageMessageWithSubtitle(String text, Image image)
{
return new Message(text, image);
}
}
Теперь из названия методов ясно для чего они нужны и какой из них использовать. Если мы захотим создавать сообщения с префиксом, то мы можем добавить новый метод.
Фабрика
Сразу видно, что ответственность создавать объекты никак не относится к основной ответственности класса Message. Если это не создает никаких трудностей, то можно оставить так, в противном случае выделяем эту ответственность в отдельный класс.
public class Message
{
public String Text{get; set;}
public Image Picture{get; set;}
public Message(String text, Image image)
{
Text = text;
Image = image;
}
}
static class MessageFactory
{
public static Message CreateTextMessage(String text)
{
return new Message(text, null);
}
public static Message CreateImageMessageWithSubtitle(String text, Image image)
{
return new Message(text, image);
}
}
Абстрактная Фабрика
У нас осталась жесткая зависимость на конкретную реализацию фабрики. Это вызовет затруднения, если мы захотим создавать сообщения каким-либо другим способом или подменять реализацию моками при тестировании.
Если нам важна сама ответственность создания объектов и мы не зависим от того, как эта ответственность реализована, используем абстрактную фабрику.
interface IMessageFactory
{
Message CreateMessage();
}
class TextMessageFactory: IMessageFactory
{
private String text;
public TextMessageFactory(String text)
{
this.text = text;
}
public Message CreateMessage()
{
return new Message(text, null);
}
}
class ImageWithSubtitleMessageFactory: IMessageFactory
{
private String text;
private Image image;
public TextMessageFactory(String text, Image image)
{
this.text = text;
this.image = image;
}
public static Message CreateMessage()
{
return new Message(text, image);
}
}
Пример с сообщениями не самый подходящий для абстрактной фабрики, но надеюсь, что он дает представление о том как она работает.
P.S. Это моя первая статья и она заняла больше времени, чем я рассчитывал. Поэтому я не включил в нее паттерны Builder и синглтон, которые собирался включить. Если статья окажется интересной, то напишу продолжение.
Комментарии (53)
lair
25.08.2015 15:42+2Из вашего примера так и не понятно, зачем же вместо простого и понятного конструктора делать фабричный метод.
(А уж делать фабрику под лозунгом «создание объектов не является обязанностью объекта» — это вообще прекрасно. А инварианты кто отслеживать будет?)pssam Автор
25.08.2015 15:57«зачем же вместо простого и понятного конструктора делать фабричный метод». Ради именования. Ради того, чтобы было понятно почему создается именно два конструктора и чем они отличаются.
«создание объектов не является обязанностью объекта». По-моему вполне соответствует SOLID. Один объект — одна ответсвенность. В данном случае ответсвенность объекта — хранение данных.lair
25.08.2015 16:05+1Наглядный пример плохого дизайна.
Во-первых, если (единственная) ответственность объекта — хранение данных, то это DTO. Придумывать для них фабрики нет никакого смысла вообще, там достаточно геттеров-сеттеров.
Во-вторых, для того, чтобы понять, чем отличаются конструкторы, достаточно правильно поименовать параметры.
Наконец, в-третьих и последних — а почему у вас вообще один и тот же класс отвечает одновременно за текстовые и графические сообщения? Разбиваете на два класса, решаете все проблемы одним махом.
PS Если вы считаете, что создание объекта — не его ответственность, то как вы решаете проблему поддержания инвариантов?
pssam Автор
25.08.2015 16:34Придумывать для них фабрики нет никакого смысла вообще
Как раз наоборот. Если вам приходится много раз использовать геттеры и сеттеры, то гораздо проще изолировать эту логику в методе, а не размазывать по всей программе.
Достаточно правильно поименовать параметры.
Не всегда это получается. Иногда появляется по 5 и больше конструкторов.
а почему у вас вообще один и тот же класс отвечает одновременно за текстовые и графические сообщения
Исключительно ради сферичности примера в вакууме.
то как вы решаете проблему поддержания инвариантов?
Честно признаюсь, что не понимаю про какую проблему Вы говорите.lair
25.08.2015 16:40Если вам приходится много раз использовать геттеры и сеттеры, то гораздо проще изолировать эту логику в методе, а не размазывать по всей программе.
… а потом делать кучу оверлоадов, чтобы инициализировать только нужные свойства, или же передаетеnull
, получая гигантские вызовы. А уж с геттерами и вовсе методы никак не помогают.
Иногда появляется по 5 и больше конструкторов.
Ну и что? Реальные проблемы возникают только тогда, когда у вас пересекаются типы параметров, и не работает оверлоадинг.
Исключительно ради сферичности примера в вакууме.
Вот этот сферический пример — это сферический пример плохой архитектуры, как делать не надо.
Честно признаюсь, что не понимаю про какую проблему Вы говорите.
Очень просто. Представьте себе класс
class Email { public string To; public string Subject; public string Text; }
Любой его экземпляр в любой момент жизни должен удовлетворять двум требованиям (собственно, это и есть инвариант):
To
иText
не содержатnull
или пустой строки- ни одно из свойств не может быть изменено после создания объекта
Внимание, вопрос: как вы удовлетворите этим требованиям, создавая объекты этого класса через фабрику?pssam Автор
25.08.2015 16:58Внимание, вопрос: как вы удовлетворите этим требованиям, создавая объекты этого класса через фабрику?
Если без извращений, то никак. Есть примеры, когда паттерны не получается применять, согласен. Но опять же, если нужно будет создавать письма с заранее предопределенным шаблоном (набором авторов, предопределенным заголовком или текстом или с заголовком, формирующимся каким-то хитрым способ), то ничто не мешает написать для этого отдельные методы, у которых из названия будет понятно какие именно письма они создают. А потом при необходимости выделить эти методы в отдельный класс.lair
25.08.2015 17:02то ничто не мешает написать для этого отдельные методы, у которых из названия будет понятно какие именно письма они создают. А потом при необходимости выделить эти методы в отдельный класс.
Вот вы и нарушили SRP.
Методы по формированию содержимого письма изначально не имеют отношения к ответственности класса, передающего сообщения. Поэтому им не место ни в нем, ни в его потенциальной (и не нужной) фабрике.
Где им место? А вы декомпонуйте тот бизнес-процесс, который у вас используется, и поймете. В худшем случае у вас там будет набор шаблонов сообщений, который, конечно, по всем признакам похож на фабрику, только семантически с сообщениями не связан никак.pssam Автор
25.08.2015 17:09Мы сейчас говорим об обстрактных примерах. В любом случае, нужно будет выделять эту логику в метод. Согласно определению в моей статье, класс, в котором он будет находиться, называется фабрикой. Если возникнет необходимость, то эта логика выделится в отдельный класс. Я тут не вижу каких-то проблем.
lair
25.08.2015 17:11Проблема в целеполагании.
У вас фабрика возникает путем выделения ответственности из конструктора. В моем же описании она возникает путем выделения повторяющейся реализации из потребителя класса. Совершенно разные задачи и ход мысли.pssam Автор
25.08.2015 17:24Я не писал код, который использует этот класс. Поэтому методы остались именно в классе сообщения.
lair
25.08.2015 17:26Вот поэтому это и некорректный пример, на который не надо ориентироваться «начинающим программистам».
pssam Автор
25.08.2015 17:28-1На примеры вообще не нужно ориентироваться. Нужно понимать что делают паттерны. А они управляют ответсвенностями.
lair
25.08.2015 17:29А зачем тогда нужны примеры, если на них не надо ориентироваться, и они неправильно показывают движение ответственностей?
pssam Автор
25.08.2015 17:38Примеры нужны для пояснения.
Этот пример правильно показывает движение ответсвенности. От конструктора в метод и в фабрику. Допустим, есть два класса, которые используют эти конструкторы и дублируют логику создания объекта, тогда возможно два пути выделения этой логики. Один — через класс сообщения, другой — через один из создающих методов. Я, лично, вполне допускаю оставить создающие методы в классе сообщения.
У нас есть сообщение и у нас есть возможность создавать предопределенные виды этого сообщения, пусть эта информация лежит в одном месте. Тем более, если мы заменяем конструкторы методами, которые поясняют, что эти конструкторы создают.lair
25.08.2015 17:40От конструктора в метод и в фабрику.
Вот это я и считаю неправильным.
А чтобы было понятно, почему, давайте на конкретных примерах разбираться, что у вас откуда и куда переходит. Хотя бы на тех же емейл-сообщениях с инвариантностью и шаблонами.
pssam Автор
25.08.2015 17:51Давайте попробуем. В примере с инвариантностью неясно откуда это требование берется и как оно используется. Допустим, нам нужен неизменяемый объект, т.е. выполняем второе требование.
class Email
{
public string To{get; private set;}
public string Subject{get; private set;}
public string Text{get; private set;}
}
Если первое требование справедливо всегда для этого класса, то просто создаем в нем конструктор.
Если оно справедливо, в рамках какого-то другого класса, то добавляем в этом классе метод, который будет создавать имейлы.
class ClassUsingNonEmptyEmails
{
private Email CreateEmail(){...}
}
Если у нас по всему приложению используются как имейлы, для которых правило 1 выполняется так и нет, то прямо в классе Email можем написать
class Email
{
static Email CreateValidEmail(){...}
}
lair
25.08.2015 17:52Вот стартовый пример класса сообщения:
public class EmailMessage { public EmailMessage(string to, string body) { if (string.IsNullOrWhiteSpace(to)) throw new ArgumentOutOfRangeException(nameof(to)); if (string.IsNullOrWhiteSpace(body)) throw new ArgumentOutOfRangeException(nameof(body)); To = to; Body = body; } public string To { get; } public string Subject { get; } public string Body { get; } }
pssam Автор
25.08.2015 17:52И что Вы с ним хотите сделать?
lair
25.08.2015 17:54А теперь давайте разбираться, как из этого класса может получиться фабрика, и зачем.
Вы утверждаете:
Но опять же, если нужно будет создавать письма с заранее предопределенным шаблоном (набором авторов, предопределенным заголовком или текстом или с заголовком, формирующимся каким-то хитрым способ), то ничто не мешает написать для этого отдельные методы, у которых из названия будет понятно какие именно письма они создают. А потом при необходимости выделить эти методы в отдельный класс.
Я хочу увидеть, как у вас это происходит.pssam Автор
25.08.2015 18:11public class EmailMessage
{
public EmailMessage(string to, string body)
{
if (string.IsNullOrWhiteSpace(to)) throw new ArgumentOutOfRangeException(nameof(to));
if (string.IsNullOrWhiteSpace(body)) throw new ArgumentOutOfRangeException(nameof(body));
To = to;
Body = body;
}
public string To { get; private set} //Убрал заголовок, вроде все равное не используется и к делу не относится.
public string Body { get; private set;}
public static CreateSpamEmail(string to)
{
return new Email(to, «Buy Me»);
}
public static CreateEmailToDaddy(string message)
{
return new Email(«Dad@dy», message);
}
public static CreateDecoratedEmail(string to, string body)
{
return new Email(to, "!!!" + body + "!!!");
}
}
Какой-то класс генерирует сообщения, но не знает кому их надо отправлять
class EmailGenerator
{
private IEmailFactory factory;
public EmailGenerator(IEmailFactory factory)
{
this.factory = factory;
}
public Email Generate()
{
return new Email(factory.Create(«RandomMessage»));
}
}
interface IEmailFactory
{
Email Create(string message);
}
class EmailToPersonFactory:IEmailFactory
{
private string to;
public EmailToPersonFactory(string to)
{
this.to = to;
}
public Email Create(string message)
{
Email.CreateDecoratedEmail(to, message);
}
}
class Spamer
{
void Run()
{
var factory = new EmailToPersonFactory(«pers@son»);
var generator = new EmailGenerator(factory);
while (true)
{
Send(generator.Generate());
}
}
}
Без конечной цели можно извращаться сколько угодно.lair
25.08.2015 18:20(какая религия вам не позволяет код форматировать?)
public class EmailMessage { //... public static CreateSpamEmail(string to) {return new Email(to, "Buy Me");} public static CreateEmailToDaddy(string message) {return new Email("Dad@dy", message);} public static CreateDecoratedEmail(string to, string body) {return new Email(to, "!!!" + body + "!!!");} //... }
Вот это — нарушение SRP. КлассEmailMessage
предназначен для того, чтобы хранить информацию о сообщении (помните, вы в начале дискуссии как раз на это упирали как на повод для вынесения фабричных методов в отдельный класс?). Откуда он знает о том, что такое «спам» или кто такой «папа»?
А должно быть вот так:
public class Order { //... private void SendEmailToCustomer(string subject, string body) { var email = new EmailMessage(Customer.GetOrderNotificationEmail(), subject, body); _emailService.Send(email); } //... public void ApproveOrder() { //... SendEmailToCustomer("Your order has been approved", "Dear customer, your order has been approved and will be processed shortly".); //... } }
pssam Автор
25.08.2015 18:25Я пока не нашел где его форматировать(
Ну, замечательно. Привели бы пример требования, тогда сразу. А что мешает выделить метод?
private Email CreateEmailToCustomer(Customer cutomer, string subject, string body)
{
return new EmailMessage(Customer.GetOrderNotificationEmail(), subject, body);
}lair
25.08.2015 18:26Он уже выделен, зачем его дальше выделять?
pssam Автор
25.08.2015 18:31Вы берете конкретный случай, где его не нужно выделять и просите, чтобы я на нем продемонстрировал, что его можно выделить. Я же не знаю всего кода и всех требований. Допустим, Вы создаете имейлы точно таким же образом в нескольких местах этого класса. А потом оказывается, что надо отправлять не по Customer.GetOrderNotificationEmail(), а по Customer.GetPrivateEmail(). Придется проверять все вызовы GetOrderNotificationEmail. Или хотите отправлять несколькими способами.
lair
25.08.2015 18:33Допустим, Вы создаете имейлы точно таким же образом в нескольких местах этого класса.
Метод, который отвечает за отправку емейла, уже выделен (и есть в видимом вам коде). Программист, который его не использует, получает вежливое предупреждение и переписывает код на правильный.
(вот методGetOrderNotificationEmail
— действительно не на своем месте, но мне было лень писать еще один метод с поиском нужного емейла среди всех емейлов покупателя, уж простите мне это)
pssam Автор
25.08.2015 18:20Ошибся в классе EmailGenerator
public Email Generate()
{
return factory.Create(«RandomMessage»);
}
}
lair
25.08.2015 18:03Забыл конструктор с
Subject
, конечно же:
public EmailMessage(string to, string subject, string body): this(to, body) { Subject = subject; }
KReal
25.08.2015 17:43- internal конструктор, например, который вызывается в фабрике, которая находится в той же сборке
- reflection
lair
25.08.2015 17:46internal конструктор, например, который вызывается в фабрике, которая находится в той же сборке
В таком случае внутри сборки инварианты не будут гарантированы. Это не всегда допустимо.
reflection
Во-первых, через reflection можно тогда и обратно сломать. А во-вторых, если нам для такой простой задачи нужен reflection, то что-то у нас не так с дизайном.KReal
25.08.2015 19:07В таком случае внутри сборки инварианты не будут гарантированы. Это не всегда допустимо.
Почему будут? Свойства пусть будут с приватными сеттерами и выставляться в конструкторе. А вот снаружи конструктора уже не будет видно, только фабрика.
lair
25.08.2015 15:56Если нам нужно создавать другие объекты (реализующие тот же интерфейс или унаследованные от базового класса) или те же объекты, но другим способом в дочерних классах, то делаем метод виртуальным и переопределяем в дочерних классах (реализуем фабричный метод).
Кстати, пример в студию. У вас в коде такого нет.
Если нам нужно использовать порождающий метод из нескольких мест, то выделяем его в отдельный класс (фабрику).
То есть если из одного метода — то можно использовать порождающий метод на классе, а если из нескольких — то обязательно на фабрике? Но почему?pssam Автор
25.08.2015 16:03Кстати, пример в студию. У вас в коде такого нет.
Вы правы. Обязательно добавлю.
То есть если из одного метода — то можно использовать порождающий метод на классе, а если из нескольких — то обязательно на фабрике? Но почему?
Имелось ввиду, что порождающий метод может быть приватным. Если он вылазит наружу и начинает использоваться из разных мест программы, то имеет смысл подумать о выделении этой ответсвенности в другой класс по тому же принципу SOLID.lair
25.08.2015 16:06Имелось ввиду, что порождающий метод может быть приватным.
Какой в нем тогда смысл? Он противоречит всему, что вы описали выше.
Если он вылазит наружу и начинает использоваться из разных мест программы, то имеет смысл подумать о выделении этой ответсвенности в другой класс по тому же принципу SOLID.
Во-первых, не SOLID, а SRP. А во-вторых, я продолжаю не понимать, каким образом корректная инициализация объекта не является его ответственностью.pssam Автор
25.08.2015 16:30Какой в нем тогда смысл?
Изабавление от дублирования, например. Именование метода. Причины для выделения метода могут быть разные.
Каким образом корректная инициализация объекта не является его ответственностью./blockquote> Допустим, у нас есть класс калькулятора и есть разные калькуляторы. Его ответственность — это делать подсчеты. Выбирать и создавать калькуляторы — ответственность другого класса.
lair
25.08.2015 16:33Изабавление от дублирования, например. Именование метода. Причины для выделения метода могут быть разные.
А вызывать-то вы его откуда будете, статический приватный метод?
Допустим, у нас есть класс калькулятора и есть разные калькуляторы. Его ответственность — это делать подсчеты. Выбирать и создавать калькуляторы — ответственность другого класса.
Выбирать — да. Это не ицинициализация, это выбор реализации (здравствуй, DIP). А вот инициализацию он должен делать самостоятельно, потому что иначе вы получите два сильно связанных класса (фабрика и собственно калькулятор).
И кстати, как только вы начинаете использовать DIP, количество фабрик (в любом виде) в вашем коде резко падает.pssam Автор
25.08.2015 16:52А вызывать-то вы его откуда будете, статический приватный метод?
Вполне возможен такой вариант. Зависит от ситуации.
А вот инициализацию он должен делать самостоятельно, потому что иначе вы получите два сильно связанных класса (фабрика и собственно калькулятор).
Не всегда объект знает, чем его инициализирует. Допустим класс принимает какое-то значение в конструкторе, а это значение вычисляется несколькими способами в разных частях программы, причем эти вычисления дублируются. Эту логику Вы никак в конструктор не запихнете. Или запихнете, используя дополнительные ненужные параметры.
как только вы начинаете использовать DIP, количество фабрик (в любом виде) в вашем коде резко падает.
При условии, что Вам нужен готовый объект, а не нужно создать его в процессе выполнения.lair
25.08.2015 16:57Вполне возможен такой вариант. Зависит от ситуации.
Так откуда вы будете его вызывать?
Не всегда объект знает, чем его инициализирует.
Всегда. Это описано в его сигнатуре.
Допустим класс принимает какое-то значение в конструкторе, а это значение вычисляется несколькими способами в разных частях программы, причем эти вычисления дублируются.
Есть два варианта.
- Это значение не имеют связи с семантикой объекта (пример: объект представляет собой емейл, а передается в него текст). Тогда вам и фабрика не нужна, а должен быть бизнес-объект, вычисляющий такие значения.
- Это значение, на самом деле, связано с семантикой объекта (пример: «правильная» конкатенация списка адресатов для емейла). Тогда это значение лучше вычислять внутри объекта
При условии, что Вам нужен готовый объект, а не нужно создать его в процессе выполнения.
И если нужно создать в процессе выполнения — тоже.Func<T>
творит чудеса.
(и да, я понимаю, что это неявная фабрика, но вся радость-то в том, что она неявная, нигде в коде этого метода нет)pssam Автор
25.08.2015 17:26— Видишь метод? — нет — и я нет, а он есть)
Лямда, конечно, творит чудеса, но сути вещей она не меняет.lair
25.08.2015 17:28Меняет. Когда я, как программист, не написал класса фабрики, то его действительно нет. Я не трачу сил на его создание или поддержку, он не добавляет семантических сущностей, которые я должен учитывать.
(и нет, лямбды тут совсем ни при чем)pssam Автор
25.08.2015 17:31За Вас это сделал компилятор. Создавать и поддерживать лямбду, которую Вы передадите все равно придется. И иногда это даже сложнее, чем поддерживать выделенный класс (у нас такое на прокете такое было), хотя бы потому, что лямбда не имеет названия и может быть непонятно, почему она делает именно то, что делает.
lair
25.08.2015 17:35Создавать и поддерживать лямбду, которую Вы передадите все равно придется.
Ээээ… нет. Потому что ее за меня создаст DI-контейнер, которым я пользуюсь.
лямбда не имеет названия и может быть непонятно, почему она делает именно то, что делает.
… а в тех случаях, когда DI-контейнер не справляется, можно сделать именованный делегат, из названия которого будет понятно, что он делает. Но это единицы случаев (даже не единицы процентов).
eaa
25.08.2015 16:32+1Проблема в том, что на простых примерах показать паттерны нереально, потому что паттерны нужны как раз тогда, когда все становится уже не так тривиально. Именно поэтому в больших книгах все начинается с UML.
А так да, в статье есть примеры, но они не дают ответы на вопросы, которые возникают… хотя есть попытка. В тексте поминается, например, что фабричный метод отдает интерфейс, а в примерах указывается только один конкретный класс. Тогда действительно не понятно, нафига эти паттерны. Ну и т.д.lair
25.08.2015 16:34+2Вполне можно придумать пример под данные конкретные паттерны. Проблема в том, что на этих примерах становится видно не только сильные стороны, но и слабые — а их автор поста обходит стороной.
Fireball222
25.08.2015 16:43+2Если нам нужно создавать объект, то используем порождающий метод.
Не хватает критериев для понимания того, когда все же можно использовать классический способ с вызовом конструктора. Если статья позиционируется для начинающих разработчиков, это просто необходимо. Мы же не хотим любой объект создавать, используя как минимум порождающий метод.
agent10
Без обид… но так и не получил ответ на ваш же вопрос «Зачем нужна еще одна статья?»
К тому же довольно в большом кол-ве источников имеются детальные примеры когда и как их применять. И не вакуумный Message, а пример реальных программ и систем.
pssam Автор
Я попытался максимально простым языком объяснить когда используются порождающие паттерны и проследить цепочку порождающий метод -> фабрика -> абстрактная фабрика. Примеры реальных программ как правило слишком сложные для начинающих программистов. В том же Гофе вначале нужно пробиться через UML, чтобы понять что вообще происходит и остается непонятным когда эти паттерны применять в реальной жизни.