SoftCraft
разноликое программирование

Top.Mail.Ru

Процедурно-параметрическая парадигма и паттерны ОО проектирования

© 2025
Александр Легалов


Содержание


Абстрактная фабрика (Abstract Factory)

Паттерн "Абстрактная фабрика" предлагается использовать в ситуациях когда:

  • система не должна зависеть от того, как создаются, компонуются и представляются входящие в нее объекты;
  • входящие в семейство взаимосвязанные объекты должны использоваться вместе и необходимо обеспечить выполнение этого ограничения;
  • система должна конфигурироваться одним из семейств составляющих ее объектов;
  • необходимо предоставить библиотеку объектов, раскрывая только их интерфейсы, но не реализацию.
      Структура паттерна Абстрактная фабрика

      Структура паттерна Абстрактная фабрика

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

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

      Объектно-ориентированная реализация Абстрактной фабрики

      Учитывая специфику данного паттерна, не будем объединять в альтернативы одного обобщения различных животных. То есть, утки отдельно, собаки отдельно. В качестве семейств при этом сформируем домашних (Domestic) животных и животных-игрушек (Toy). Исходя из этого появляются некоторые абстрактные утки и собаки:

        // Абстрактная утка, от которой наследуюют утки разных семейств.
        class Duck {
          public:
          Duck() {std::cout << "Duck created! ";}
        };
        // Абстрактная собака, от которой наследуют собаки разных семейств
        class Dog {
          public:
          Dog() {std::cout << "Dog created! ";}
        };
      

      Они используются для формирования конкретных уток и собак различных семейств: домашних животных и игрушек.

        //-----------------------------------------------
        // Семейство домашних животных
        //-----------------------------------------------
        // Домашняя утка
        class DomesticDuck: public Duck {
        public:
          DomesticDuck(): Duck() {
            std::cout << "It is Domestic duck.\n";
          }
        };
        // Домашняя собака
        class DomesticDog: public Dog {
          public:
          DomesticDog(): Dog() {
            std::cout << "It is Domestic dog.\n";
          }
        };
      
        //-----------------------------------------------
        // Семейство животных - игрушек
        //-----------------------------------------------
        // Игрушечная утка
        class ToyDuck:public Duck {
        public:
          ToyDuck(): Duck() {
            std::cout << "It is Toy duck.\n";
          }
        };
        // Игрушечная собака
        class ToyDog:public Dog {
        public:
          ToyDog(): Dog() {
              std::cout << "It is Toy dog.\n";
          }
        };
      

      Для создания экземпляров различных семейств используются конкретные фабрики животных, порождаемые об общего родителя, являющегося абстрактной фабрикой:

        // Абстрактная фабрика, используемая для порождения фабрик различных семейств.
        class AbstractFactory {
        public:
          virtual Duck *CreateDuck() const = 0;
          virtual Dog  *CreateDog()  const = 0;
        };
      
        // Фабрика домашних животных
        class DomesticFactory : public AbstractFactory {
        public:
          virtual Duck *CreateDuck() const override {
            return new DomesticDuck();
          }
          virtual Dog *CreateDog() const override {
              return new DomesticDog();
          }
        };
      
        // Фабрика животных - игрушек
        class ToyFactory : public AbstractFactory {
        public:
          virtual Duck *CreateDuck() const override {
            return new ToyDuck();
          }
          virtual Dog *CreateDog() const override {
            return new ToyDog();
          }
        };
      

      Использование сформированных программных объектов обеспечивает полиморфное и независимое формирование различных семейств альтернативных животных.

      Абстрактная фабрика как отношения между данными и функциями

      Если сравнить Абстрактную фабрику и Фабричный метод, то можно увидеть много общего. В частности альтернативы Фабричного метода напоминают альтернативы семейств. Отличие проявляется в том, что интерфейсные классы ОО программы, используемые в качестве создателей отличаются количеством методов порождающих конкретные объекты. Это говорит об увеличении числа обобщений, с каждым из которых связан одинаковый по количеству набор альтернатив. Отношение между функциями и данными, определяющие соответствующие отношения для животных приведены на рисунке.

      Графическое представление ПП аналога Абстрактной фабрики

      Графическое представление ПП аналога Абстрактной фабрики

      Процедурно-параметрическая имитация Абстрактной фабрики

      Если опираться на приведенную схему, то ПП парадигма позволяет сформировать как абстрактные фабрики, так и семейства продуктов с использованием обобщений и специализаций. При этом по своей сути абстрактные фабрики, как и создатели, реализуемые в ПП аналоге паттерна Фабричный метод, могут состоять из специализаций, реализованных в как эволюционно расширяемый перечислимый тип. Каждое вид животных содержит свой набор специализаций, эквивалентный набору других семейств. Обобщения выступают в роли клея для альтернатив по одним и тем же животным. Создатели являются внешними параметрическими функциями, ориентированными на создание конкретных животных, принадлежащих к конкретным категориям. Отдельные категории задаются обобщающими аргументами, определяющими используемую фабрику.

      В рассматриваемом примере каждое из семейств может иметь свои варианты животных (домашних и игрушечных), которые в дальнейшем могут использоваться в качестве основ специализаций для обобщений видов.

        //-------------------------------------------------------
        // Семейство домашних животных
        // Домашняя утка
        typedef struct DomesticDuck {} DomesticDuck;
        void DomesticDuckMessage() {printf("It is Domestic duck.\n");}
        // Домашняя собака
        typedef struct DomesticDog {} DomesticDog;
        void DomesticDogMessage() {printf("It is Domestic dog.\n");}
      
        //-------------------------------------------------
        // Семейство животных - игрушек
        // Игрушечная утка
        typedef struct ToyDuck {} ToyDuck;
        void ToyDuckMessage() {printf("It is Toy duck.\n");}
        // Игрушечная собака
        typedef struct ToyDog {} ToyDog;
        void ToyDogMessage() {printf("It is Toy dog.\n");}
      

      Обобщения могут использовать эти основы, образуя соответствующие специализации:

        // Абстрактная утка, обобщающая разные семейства.
        typedef struct Duck {}<> Duck;
        void DuckMessage() {printf("Duck created! ");}
        // Специализации уток
        Duck + <DomesticDuck;>;
        Duck + <ToyDuck;>;
      
        // Абстрактная собака, обобщающая разные семейства
        typedef struct Dog {}<> Dog;
        void DogMessage() {printf("Dog created! ");}
        // Специализации собак
        Dog + <DomesticDog;>;
        Dog + <ToyDog;>;
      

      Абстрактная фабрика определяется через обобщение, имитирующее эволюционно расширяемыей перечислимый тип. Для каждой из сформированных специализаций создается обработчик, осуществляющий создание различных животных реализованных к текущему моменту времени.

        // Обобщающая функция, определяющая создание уток.
        Duck* CreateDuck<AbstractFactory* f>() {return NULL;} //= 0;
        //Обработчик, обеспечивающий создание домашней утки.
        Duck* CreateDuck<AbstractFactory.ForDomestic* f>() {
          struct Duck.DomesticDuck* d = create_spec(Duck.DomesticDuck);
          DuckMessage();
          DomesticDuckMessage();
          return (Duck*)d;
        }
        //Обработчик, обеспечивающий создание игрушечной утки.
        Duck* CreateDuck<AbstractFactory.ForToy* f>() {
          struct Duck.ToyDuck* d = create_spec(Duck.ToyDuck);
          DuckMessage();
          ToyDuckMessage();
          return (Duck*)d;
        }
      
        // Обобщающая функция, определяющая создание собак.
        Dog* CreateDog<AbstractFactory* f>() {return NULL;} //= 0;
        //Обработчик, обеспечивающий создание домашней собаки.
        Dog* CreateDog<AbstractFactory.ForDomestic* f>() {
          struct Dog.DomesticDog* d = create_spec(Dog.DomesticDog);
          DogMessage();
          DomesticDogMessage();
          return (Dog*)d;
        }
        //Обработчик, обеспечивающий создание игрушечной собаки.
        Dog* CreateDog<AbstractFactory.ForToy* f>() {
          struct Dog.ToyDog* d = create_spec(Dog.ToyDog);
          DogMessage();
          ToyDogMessage();
          return (Dog*)d;
        }
      

      Абстрактная фабрика и ППП

      Моделирование абстрактной фабрики с использованием ПП подхода позволяет практически полностью повторить ОО решение. Помимо этого можно отметить более гибкие возможности, связанные с расширением номенклатуры продуктов. В частности, при добавлении нового продукта в объектно--ориентированной программе необходимо изменить интерфейсы базовой и производных Абстрактных фабрик. Предлагаемый GoF [gof-patternsГамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Паттерны объектно-ориентированного проектирования. - СПб.: Питер, 2020. - 448 с.] вариант, связанный с передачей параметра, идентифицирующего добавляемую фабрику, вряд ли можно считать надежным. Использование ПП подхода позволяет безболезненно изменять как количество фабрик, добавляя новый эволюционно расширяемый перечислимый тип, так и добавлять под этот тип новый обработчик специализации.


      Содержание