Пример реализации паттерна ООП Абстрактная фабрика инструментами MQL5

 
В ООП существует паттерн Абстрактная фабрика.

Я составил скрипт, который представляет собой попытку реализовать этот паттерн в MQL5.

Демонстрация паттерна осуществлена на примере создания завода алкогольной и безалкогольной продукции.

Абстрактная фабрика создает абстрактные бутылки и жидкости.

Заводы создают соответственно вино, лимонад, бутылки под вино и лимонад.

В конфигураторе все настраивается. Для этого есть специальный класс.

В общем-то код с комментариями. Там все понятно.

//*******************************************************************
//--- АБСТРАКТНАЯ ТАРА
interface ABottle
  {
   //--- наполнить 
   void Fill();
  };


//*******************************************************************
//--- АБСТРАКТНАЯ ЖИДКОСТЬ
interface ALiquid {};



//*******************************************************************
//--- АБСТРАКТНАЯ ФАБРИКА ПРОДУКЦИИ
interface AFactory 
  {
   //--- АБСТРАКТНАЯ ФАБРИКА УМЕЕТ СОЗДАВАТЬ 
   //--- БУТЫЛКИ
   ABottle * CreateBottle(); 
   //--- И НАПИТКИ
   ALiquid * CreateLiquid();
  };



//*******************************************************************
//--- БУТЫЛКА ПОД ВИНО ОТВЕЧАЕТ СПЕЦИФИКАЦИИ БУТЫЛОК
class CWineBottle : public ABottle
  {
public:
   //--- наполнить
   void Fill() { Print( "Бутылка для вина наполнена" ); }
                     
                     CWineBottle( void ) { Print( "Сделана бутылка для вина" ); }
                    ~CWineBottle( void ) { Print( "Уничтожена бутылка для вина" ); }
  };

//*******************************************************************
//--- БУТЫЛКА ПОД ЛИМОНАД ТОЖЕ ОТВЕЧАЕТ СПЕЦИФИКАЦИИ БУТЫЛОК
class CBeverageBottle : public ABottle
  {
public:
   //--- наполнить
   void Fill() { Print( "Бутылка для лимонада наполнена" ); }
                     
                     CBeverageBottle( void ) { Print( "Сделана бутылка для лимонада" ); }
                    ~CBeverageBottle( void ) { Print( "Уничтожена бутылка для лимонада" ); }
  };



//*******************************************************************
//--- ВИНО ОТВЕЧАЕТ СПЕЦИФИКАЦИИ ЖИДКОСТЕЙ
class CWine : public ALiquid
  {
public:
                     CWine( void ) { Print( "Сделано вино" ); }
                    ~CWine( void ) { Print( "Уничтожено вино :(" ); }
  };
//*******************************************************************
//--- ЛИМОНАД ОТВЕЧАЕТ СПЕЦИФИКАЦИИ ЖИДКОСТЕЙ
class CBeverage : public ALiquid
  {
public:
                     CBeverage( void ) { Print( "Сделан лимонад" ); }
                    ~CBeverage( void ) { Print( "Уничтожен лимонад" ); }
  };





//*******************************************************************
//--- ФАБРИКА АЛКОГОЛЬНОЙ ПРОДУКЦИИ ОТВЕЧАЕТ СПЕЦИФИКАЦИИ 
//--- АБСТРАКТНОЙ ФАБРИКИ И БУДЕТ ДЕЛАТЬ БУТЫЛКИ И НАПИТКИ
class CAlcoholFactory : public AFactory
  {
public:
   //--- ДЛЯ ВИНА
   ABottle * CreateBottle() { return new CWineBottle(); }   
   //--- И САМО ВИНЦО
   ALiquid * CreateLiquid() { return new CWine(); }
                     
                     CAlcoholFactory( void ) { Print( "Создана алкогольная фабрика" ); }
                    ~CAlcoholFactory( void ) { Print( "Уничтожена алкогольная фабрика" ); }
  };



//*******************************************************************
//--- ФАБРИКА БЕЗАЛКОГОЛЬНОЙ ПРОДУКЦИИ ОТВЕЧАЕТ СПЕЦИФИКАЦИИ 
//--- АБСТРАКТНОЙ ФАБРИКИ И ТОЖЕ БУДЕТ ДЕЛАТЬ БУТЫЛКИ И НАПИТКИ
class CBeverageFactory : public AFactory
  {
public:
   //--- ДЛЯ ЛИМОНАДА
   ABottle * CreateBottle() { return new CBeverageBottle(); }   
   //--- И ЛИМОНАД
   ALiquid * CreateLiquid() { return new CBeverage(); }
                     
                     CBeverageFactory( void ) { Print( "Создана фабрика безалкогольных напитков" ); }
                    ~CBeverageFactory( void ) { Print( "Уничтожена фабрика безалкогольных напитков" ); }
  };




//*******************************************************************
//--- КОНФИГУРАТОР ФАБРИКИ
class CConfigurator
  {
   ALiquid *m_liquid;
   ABottle *m_bottle;
   AFactory *m_factory;
   
public:
   void Run() { m_bottle.Fill(); }
                     template < typename FactoryType >
                     CConfigurator( FactoryType * factory ) 
                       {
                        m_factory = factory;
                        m_bottle = m_factory.CreateBottle();
                        m_liquid = m_factory.CreateLiquid();
                       }
                     CConfigurator( void );
                    ~CConfigurator( void ) 
                       { 
                        delete m_liquid; 
                        delete m_bottle; 
                        delete m_factory;
                       };
  };



   

//*******************************************************************
void OnStart()
  {
   //--- ПОСТРОИТЬ И ПУСТИТЬ В ЭКСПЛУАТАЦИЮ ВИНЗАВОД
   CConfigurator factoryOne( new CAlcoholFactory );
   factoryOne.Run();
   
   //--- ПОСТРОИТЬ И ПУСТИТЬ В ЭКСПЛУАТАЦИЮ ЗАВОД БЕЗАЛКОГОЛЬНЫХ НАПИТКОВ
   CConfigurator factoryTwo( new CBeverageFactory );
   factoryTwo.Run();
  }



В результате запуска скрипта получим такой текст:


Создана алкогольная фабрика
Сделана бутылка для вина
Сделано вино
Бутылка для вина наполнена
Создана фабрика безалкогольных напитков
Сделана бутылка для лимонада
Сделан лимонад
Бутылка для лимонада наполнена
Уничтожен лимонад
Уничтожена бутылка для лимонада
Уничтожена фабрика безалкогольных напитков
Уничтожено вино :(
Уничтожена бутылка для вина
Уничтожена алкогольная фабрика


Файлы:
 

Очень оригинально, и продуманно :)

 

Особо радует последняя строчка сообщения))

 

Браво. Хорошая иллюстрация.

Я обычно в интерфейсах всех функции приравниваю нулю.
 

В конфигураторе шаблон не нужен. У вас же фабрики реализуют интерфейс, вот его можно и прописывать.

 

Интерфейсы в MQL профанация чистой воды, т.к. by design, интерфесы это горизонтальные связи (контракты), но множественное наследование интерфейсов в MQL запрещено.

 

UPD:

Комбинатор верно указал:

                     template < typename FactoryType >
                     CConfigurator( FactoryType AFactory *factory ) 
                       {
                        m_factory = factory;
                        m_bottle = m_factory.CreateBottle();
                        m_liquid = m_factory.CreateLiquid();
                       }
Однако, шаблон необходим, если вместо interface использовать class.
 
dmipec:

UPD:

Комбинатор верно указал:

Однако, шаблон необходим, если вместо interface использовать class.

В MQL нет разницы между абстрактным классом и интерфейсом. Сейчас интерфейсы в MQL это просто нелепица.

 

Не понятно, почему фабрика не сделана шаблоном с типами бутылки и напитка, или без шаблона но с конструктором, принимающем инстансы нужных типов. При расширении номенклатуры бутылок и напитков - плодить для каждого новый фактически пустой класс специальной фабрики - не комильфо. Тем более, что бутылок и напитков может быть много разных, а число сочетаний будет расти в геометрической прогрессии.

PS. А вообще-то сайт вроде про алготрейдинг. Какие нафиг бутылки и напитки? Что за намек? ;-)

 
Stanislav Korotky:

Не понятно, почему фабрика не сделана шаблоном с типами бутылки и напитка, или без шаблона но с конструктором, принимающем инстансы нужных типов. При расширении номенклатуры бутылок и напитков - плодить для каждого новый фактически пустой класс специальной фабрики - не комильфо. Тем более, что бутылок и напитков может быть много разных, а число сочетаний будет расти в геометрической прогрессии.

PS. А вообще-то сайт вроде про алготрейдинг. Какие нафиг бутылки и напитки? Что за намек? ;-)


Совершенно верно. Это недостаток паттерна. Воспроизведена каноническая структура. 

 

А в чем смысл? Что теперь с этим делать?