Рассмотрение структурных шаблонов 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Рассмотрение структурных шаблонов



Лабораторная работа №21-23

Шаблоны проектирования

 

Цель работы: ознакомиться с основными шаблонами проектирования, научиться применять их при проектировании и разработке ПО.

Теоретические сведения

Шаблон проектирования или паттерн (англ. design pattern) в разработке программного обеспечения — повторимая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста.

Обычно шаблон не является законченным образцом, который может быть прямо преобразован в код; это лишь пример решения задачи, который можно использовать в различных ситуациях.

Объектно-ориентированные шаблоны показывают отношения и взаимодействия между классами или объектами, без определения того, какие именно конечные классы или объекты приложения будут

использоваться.

Сообразное использование паттернов проектирования дает разработчику ряд неоспоримых преимуществ.

Приведем некоторые из них.

1) Модель системы, построенная в терминах паттернов проектирования, фактически является структурированным выделением тех элементов и связей, которые значимы при решении поставленной задачи.

2) Помимо этого, модель, построенная с использованием паттернов проектирования, более проста и наглядна в изучении, чем стандартная модель.

3) Тем не менее, несмотря на простоту и наглядность, она позволяет глубоко и всесторонне проработать архитектуру разрабатываемой системы с использованием специального языка.

4) Применение паттернов проектирования повышает устойчивость системы к изменению требований и упрощает неизбежную последующую доработку системы.

5) Кроме того, трудно переоценить роль использования паттернов при интеграции информационных систем организации.

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

7) Но самое главное любой шаблон проектирования может стать палкой о двух концах: если он будет применен не к месту, это может обернуться катастрофой и создать вам много проблем в последующем.

8) В то же время, реализованный в нужном месте, в нужное время, он может стать для вас настоящим спасителем.

Есть три основных вида шаблонов проектирования:

● структурные;

● порождающие;

● поведенческие.

Структурные шаблоны

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

Порождающие шаблоны

Порождающие шаблоны проектирования, которые абстрагируют процесс инстанцирования.

Они позволяют сделать систему независимой от способа создания, композиции и представления объектов.

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

Поведенческие шаблоны

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

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

Рассмотрение структурных шаблонов

Адаптер (Adapter)

Адаптер (Adapter)преобразует интерфейс класса в некоторый другой интерфейс, ожидаемый клиентами.

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

Адаптер уровня класса использует множественное наследование (интерфейс наследуется от одного класса, а реализация — от другого),

а в адаптере уровня объекта применяется композиция объектов (как правило, в объекте определяется ссылка на другой объект).

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

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

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

 

 

 

Класс Adapter приводит интерфейс класса Adaptee в соответствие с интерфейсом класса Target (наследником которого является Adapter).

Это позволяет объекту Client использовать объект Adaptee (посредством адаптера Adapter) так, словно он является экземпляром класса Target.

Таким образом Client обращается к интерфейсу Target, реализованному в наследнике Adapter, который перенаправляет обращение к Adaptee.

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

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

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

 

Реализация на C#

using System;

 

  namespace Adapter

 {

class MainApp

{

static void Main()

{

// Create adapter and place a request

Target target = new Adapter();

target.Request();

 

// Wait for user

Console.Read();

}

}

 

// "Target"

 

class Target

{

public virtual void Request()

{

Console.WriteLine("Called Target Request()");

}

}

 

// "Adapter"

 

class Adapter: Target

{

private Adaptee adaptee = new Adaptee();

 

public override void Request () /* Модификатор override требуется для расширения или изменения абстрактной или виртуальной реализации унаследованного метода, свойства, индексатора или события */

{

// Possibly do some other work

// and then call SpecificRequest

adaptee.SpecificRequest();

}

}

 

// "Adaptee"

 

class Adaptee

{

public void SpecificRequest()

{

Console.WriteLine("Called SpecificRequest()");

}

}

 }

 

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

Вариант решения данной проблемы с использованием шаблона Адаптер показан на рисунке.

 

Рис. 1

 

Фасад (Facade)

Фасад (Facade)предоставляет унифицированный интерфейс к множеству интерфейсов в некоторой подсистеме. Определяет интерфейс более высокого уровня, облегчающий работу с подсистемой.

Фасад позволит отделить подсистему как от клиентов, так и от других подсистем, что, в свою очередь, способствует повышению степени независимости и переносимости.

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

Клиенты общаются с подсистемой, посылая запросы фасаду.

Шаблон “фасад” структурный шаблон проектирования, позволяющий скрыть сложность системы путем сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.

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

Решение: определить одну точку взаимодействия с подсистемой — фасадный объект, обеспечивающий общий интерфейс с подсистемой, и возложить на него обязанность по взаимодействию с её компонентами.

Фасад — это внешний объект, обеспечивающий единственную точку входа для служб подсистемы. Реализация других компонентов подсистемы закрыта и не видна внешним компонентам.

На C#

using System;

Namespace Library

{

class SubsystemA

{

   public string OperationA1()

   {

       return "Subsystem A, Method A1\n";

   }

   public string OperationA2()

   {

       return "Subsystem A, Method A2\n";

   }

}

class SubsystemB

{

   public string OperationB1()

   {

       return "Subsystem B, Method B1\n";

   }

   public string OperationB2()

   {

       return "Subsystem B, Method B2\n";

   }

}

class SubsystemC

{

   public string OperationC1()

   {

       return "Subsystem C, Method C1\n";

   }

   public string OperationC2()

   {

       return "Subsystem C, Method C2\n";

   }

}

 public class Facade

{

   private SubsystemA a = new SubsystemA();

   private SubsystemB b = new SubsystemB();

  private SubsystemC c = new SubsystemC();

   public void Operation1()

   {

       Console.WriteLine("Operation 1\n" +

       a.OperationA1() +

       a.OperationA2() +

       b.OperationB1());

   }

   public void Operation2()

   {

       Console.WriteLine("Operation 2\n" +

       b.OperationB2() +

       c.OperationC1() +

       c.OperationC2());

   }

}

}

Class Program

{

static void Main(string[] args)

{

   Facade.Operation1();

   Facade.Operation2();

   // Wait for user

   Console.Read();

}

}

// пример

Namespace Sample

{

// The 'Subsystem ClassA' class

class CarModel

{

   public void SetModel()

   {

       Console.WriteLine(" CarModel - SetModel");

   }

}

/// <summary>

/// The 'Subsystem ClassB' class

/// </summary>

class CarEngine

{

   public void SetEngine()

   {

       Console.WriteLine(" CarEngine - SetEngine");

   }

}

// The 'Subsystem ClassC' class

class CarBody

{

   public void SetBody()

   {

       Console.WriteLine(" CarBody - SetBody");

   }

}

// The 'Subsystem ClassD' class

class CarAccessories

{

   public void SetAccessories()

   {

       Console.WriteLine(" CarAccessories - SetAccessories");

   }

}

// The 'Facade' class

public class CarFacade

{

   private CarModel model;

   private CarEngine engine;

   private CarBody body;

   private CarAccessories accessories;

   public CarFacade()

   {

       model = new CarModel();

       engine = new CarEngine();

       body = new CarBody();

       accessories = new CarAccessories();

   }

   public void CreateCompleteCar()

   {

       Console.WriteLine("******** Creating a Car **********\n");

       model.SetModel();

       engine.SetEngine();

       body.SetBody();

       accessories.SetAccessories();

      Console.WriteLine("\n***** Car creation is completed*****");

   }

}

// Facade Pattern Demo

class Program

{

   static void Main(string[] args)

   {

       CarFacade facade = new CarFacade();

       facade.CreateCompleteCar();

       Console. Read ();

   }

}

}

У паттерна фасад есть следующие преимущества:

1. Изолирует клиентов от компонентов подсистемы
Уменьшая тем самым число объектов, с которыми клиентам приходится иметь дело, упрощая работу с подсистемой.

2. Позволяет ослабить связанность между подсистемой и ее клиентами.
Зачастую компоненты подсистемы сильно связаны: многие классы из разных частей и слоев системы взаимодействуют с друг другом, меняя один такой класс, приходится перекомпилировать и остальные связанные и т.д. Слабая связанность позволяет видоизменять компоненты, не затрагивая при этом клиентов. Фасады помогают разложить систему на слои и структурировать зависимости между объектами, а также избежать сложных и циклических зависимостей. Это оказывается важным, если клиент и подсистема реализуются независимо.
Уменьшение числа зависимостей на стадии компиляции чрезвычайно важно в больших системах. Хочется, конечно, чтобы время, уходящее на перекомпиляцию после изменения классов подсистемы, было минимальным. Сокращение числа зависимостей за счет фасадов может уменьшить количество нуждающихся в повторной компиляции файлов после небольшой модификации какой-нибудь важной подсистемы. Фасад может также упростить процесс переноса системы на другие платформы, поскольку уменьшается вероятность того, что в результате изменения одной подсистемы понадобится изменять и все остальные.

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

Заместитель (Proxy)

Применимость

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

Вот несколько типичных ситуаций, где заместитель оказывается полезным:

  • удаленный заместитель предоставляет локального представителя вместо объекта, находящегося в другом адресном пространстве;
  • виртуальный заместитель создает «тяжелые» объекты по требованию;
  • защищающий заместитель контролирует доступ к исходному объекту. Такие заместители полезны, когда для разных объектов определены различные права доступа;
  • “умная” ссылка - это замена обычного указателя. Она позволяет выполнить дополнительные действия при доступе к объекту. К типичным применениям такой ссылки можно отнести:

o подсчет числа ссылок на реальный объект, с тем чтобы занимаемую им память можно было освободить автоматически, когда не останется ни одной ссылки (такие ссылки называют еще «умными» указателями);

o загрузку объекта в память при первом обращении к нему;

o проверку и установку блокировки на реальный объект при обращении к нему, чтобы никакой другой объект не смог в это время изменить его.

Отношения

Proxy при необходимости переадресует запросы объекту RealSubject. Детали зависят от вида заместителя.

Родственные паттерны

Паттерн адаптер предоставляет другой интерфейс к адаптируемому объекту.

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

 Несколько замечаний относительно декоратора. Хотя его реализация и похожа на реализацию заместителя, но назначение совершенно иное.

Декоратор добавляет объекту новые обязанности, а заместитель контролирует доступ к объекту. Степень схожести реализации заместителей и декораторов может быть различной.

Защищающий заместитель мог бы быть реализован в точности как декоратор.

С другой стороны, удаленный заместитель не содержит прямых ссылок на реальный субъект, а лишь косвенную ссылку, что-то вроде «идентификатор хоста и локальный адрес на этом хосте».

Пример реализации
“->” и “.” операторы дают разный результат.

Заместитель (Proxy) замещает другой объект для обеспечения контроля доступа к нему.

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

Class IMath

{

public:

virtual double add(double, double) = 0;

virtual double sub(double, double) = 0;

virtual double mul(double, double) = 0;

virtual double div(double, double) = 0;

};

/**

 * "Real Subject"

 */

Class Math: public IMath

{

public:

virtual double add(double x, double y)

{

   return x + y;

}

virtual double sub(double x, double y)

{

   return x - y;

}

virtual double mul(double x, double y)

{

   return x * y;

}

virtual double div(double x, double y)

{

   return x / y;

}

};

/**

 * "Proxy Object"

 */

Int main()

{

// Create math proxy

IMath *proxy = new MathProxy();

// Do the math

cout << "4 + 2 = " << proxy->add(4, 2) << endl;

cout << "4 - 2 = " << proxy->sub(4, 2) << endl;

cout << "4 * 2 = " << proxy->mul(4, 2) << endl;

cout << "4 / 2 = " << proxy->div(4, 2) << endl;

delete proxy;

return 0;

}

Декоратор (Decorator)

 

Порождающие шаблоны

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

 Клиент пользуется только интерфейсами, заданными в классах «абстрактная фабрика» и «абстрактный продукт».

 

 

 

 

Реализация на C++

//общий интерфейс для данных любого формата

class FormattedData {

//...

};

// конкретный формат

class FormattedData1: public FormattedData {

//...

};

// другой конкретный формат

class FormattedData2: public FormattedData {

//...

};

// абстрактная фабрика

class AbstractFactory {

public:

virtual FormattedData * createFormattedData () = 0;

};

//фабрика для создания объектов, содержащих данные

// нужного формата

class FormatFactory: public AbstractFactory {

public:

FormattedData* createFormattedData() {

if(/* some condition */) {

    return new FormattedData1();

}

else {

    return new FormattedData2();

}

}

};

//использование абстрактной фабрики

int main () {

FormatFactory* pfactory = new FormatFactory();

FormattedData* pdata = pfactory->createFormattedData();

//send data

//...

if(pfactory)delete pfactory;

if(pdata)delete pdata;

return 0;}

Еще пример

#include <iostream> #include <vector> // Абстрактные базовые классы всех возможных видов воинов class Infantryman   //пехотинец { public: virtual void info() = 0; virtual ~Infantryman() {} }; class Archer    // стрелок, лучник { public: virtual void info() = 0; virtual ~Archer() {} }; class Horseman // кавалерист { public: virtual void info() = 0; virtual ~ Horseman () {} }; // Конкретные классы всех видов воинов Римской армии class RomanInfantryman: public Infantryman { public: void info() { cout << "RomanInfantryman" << endl; } }; class RomanArcher: public Archer { public: void info() { cout << "RomanArcher" << endl; } }; class RomanHorseman: public Horseman { public: void info() { cout << "RomanHorseman" << endl; } }; // Классы всех видов воинов армии Карфагена class CarthaginianInfantryman: public Infantryman { public: void info() { cout << "CarthaginianInfantryman" << endl; } }; class CarthaginianArcher: public Archer { public: void info() { cout << "CarthaginianArcher" << endl; } }; class CarthaginianHorseman: public Horseman { public: void info() { cout << "CarthaginianHorseman" << endl; } }; // Абстрактная фабрика для производства воинов class ArmyFactory { public: virtual Infantryman* createInfantryman() = 0; virtual Archer* createArcher() = 0; virtual Horseman* createHorseman() = 0; virtual ~ArmyFactory() {} }; // Фабрика для создания воинов Римской армии class RomanArmyFactory: public ArmyFactory { public: Infantryman* createInfantryman() { return new RomanInfantryman; } Archer* createArcher() { return new RomanArcher; } Horseman* createHorseman() { return new RomanHorseman; } }; // Фабрика для создания воинов армии Карфагена class CarthaginianArmyFactory: public ArmyFactory { public: Infantryman* createInfantryman() { return new CarthaginianInfantryman; } Archer* createArcher() { return new CarthaginianArcher; } Horseman* createHorseman() { return new CarthaginianHorseman; } }; // Класс, содержащий всех воинов той или иной армии class Army { public: ~Army() { int i; for(i=0; i<vi.size(); ++i) delete vi[i]; for(i=0; i<va.size(); ++i) delete va[i]; for(i=0; i<vh.size(); ++i) delete vh[i]; } void info() { int i; for(i=0; i<vi.size(); ++i) vi[i]->info(); for(i=0; i<va.size(); ++i) va[i]->info(); for(i=0; i<vh.size(); ++i) vh[i]->info(); } vector<Infantryman*> vi; vector<Archer*> va; vector<Horseman*> vh; }; // Здесь создается армия той или иной стороны class Game { public: Army* createArmy(ArmyFactory& factory) { Army* p = new Army; p->vi.push_back(factory.createInfantryman()); p->va.push_back(factory.createArcher()); p->vh.push_back(factory.createHorseman()); return p; } }; int main() { Game game; RomanArmyFactory ra_factory; CarthaginianArmyFactory ca_factory; Army * ra = game.createArmy(ra_factory); Army * ca = game.createArmy(ca_factory); cout << "Roman army:" << endl; ra->info(); cout << "\nCarthaginian army:" << endl; ca->info(); //... }

Вывод программы будет следующим:

  Roman army: RomanInfantryman RomanArcher RomanHorseman Carthaginian army: CarthaginianInfantryman CarthaginianArcher CarthaginianHorseman  

Для данного паттерна так же подходит другое название — виртуальный конструктор.

Создание конкретной фабрики


     Для начала, реализуем конкретную фабрику, не используя шаблоны, чтобы понять, как же она должна работать.

    И так, нам нужен класс, который сможет создавать объекты, поддерживающие единый интерфейс.

Назовем этот интерфейс Foo, а фабрику соответственно FooFactory. В качестве идентификатора для создания объекта возьмем строку.

С lass FooFactory

{ public:

 FooFactory();

~virtual FooFactory();

Foo * create(const std::string & id) const;    

};

А как же мы будем добавлять классы и собственно создавать их экземпляры?

     В С++ есть шаблоны, которые и помогут решить эту не простую задачу.

Для создания объектов типа Foo мы будем использовать другие объекты, вспомогательные, которые возьмут на себя работу по созданию объектов нужного нам класса.

В простонародье такие классы называют creator.

Создается абстрактный класс creator, у которого обычно есть только один метод — создание объекта определенного интерфейса, в текущем варианте объекты типа Foo.

А на его основе уже создаются конкретные классы creator'ы.

Благодаря абстрактному классу creator, мы можем хранить набор конкретных creator'ов в любом контейнере.

В данном случае шаблоны позволяют генерировать код для конкретного creator'а на основе параметра шаблона.

Абстракный класс для создания объектов типа Foo:

 

Class abstractFooCreator

{

public:

    virtual fooCreator() {}

    virtual Foo * create() const = 0;

};

И шаблонный класс, в котором будет генерироваться собственно код для создания объекта конкретного класса:

 

template <class C>

Class abstractFooCreator

{

Public:

virtual fooCreator() {}

    virtual Foo * create() const = 0;

};

template <class C>

С lass FooFactory

{

protected:

    typedef std::map<std::string, abstractFooCreator*> FactoryMap;

    FactoryMap _factory;

public:

    FooFactory();

    ~virtual FooFactory();

    template <class C>

    void add(const std::string & id)

    {

              typename FactoryMap::iterator it = _factory.find(id);

              if (it == _factory.end())

                       _factory[id] = new fooCreator<C>();

    }

    Foo * create(const std::string & id)

    {

              typename FactoryMap::iterator it = _factory.find(id);

              if (it!= _factory.end())

                       return it->second->create();

              return NULL;

    }

};

Пример, как данную фабрику использовать. Добавление классов в фабрику:

FooFactory factory;

factory.add<MyFoo>("MyFoo");

factory.add<MyFoo2>("MyFoo2");

factory.add<ImprovedFoo>("ImprovedFoo");

Создание объекта с помощью фабрики:

Foo * p = factory.create("MyFoo2");

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

 

Создание шаблонной фабрики

 

Ну, а теперь создадим, на основе этого конкретного примера, вполне конкретный паттерн.

 

Что нужно нашей фабрике, чтобы удовлетворять наши возможности? Конечно, это задание типа идентификатора, который можем быть как и enum'ом, так и std::string или любым другим типом подходящим для идентификатора.

Второе — это собственно тип объектов, которые мы будем создавать, роль которого в примере выше исполнял класс Foo.

 

template <class Base, class IdType>

Class ObjectFactory

{

public:

    ObjectFactory() {}

    virtual ~ObjectFactory();

    template <class C>

    void add(const IdType & id);

    Base * create() const;

};

 

Вот так будет выглядеть наша шаблонная фабрика.

Приступим к ее реализации.

Как и в примере с Foo, нам нужно решить проблему создания объектов конкретного типа.

То есть нам нужен абстрактный класс, у которого есть метод для создания объекта типа Base, и конкретный creator класс, который наследует этот абстрактный класс и реализует этот метод, используя шаблонный параметр.

 

template <class Base>

Class AbstractCreator

{

public:

    AbstractCreator() {}

    virtual ~AbstractCreator(){}

    virtual Base* create() const = 0;

};

template <class C, class Base>

class Creator: public AbstractCreator<Base>

{

public:

    Creator() { }

    virtual ~Creator() {}

    Base * create() const { return new C(); }

};

Ну, а теперь нужно лишь использовать эти классы в нашей фабрике.

 

template <class Base, class IdType>

Class ObjectFactory

{

protected:

    typedef AbstractCreator<Base> AbstractFactory;

    typedef std::map<IdType, AbstractFactory*> FactoryMap;

    FactoryMap    _factory;

public:

    ObjectFactory() {}

    virtual ~ObjectFactory();

    template <class C>

    void add(const IdType & id)

    {

              registerClass(id, new Creator<C, Base>());

    }

protected:

    void registerClass(const IdType & id, AbstractFactory * p)

    {

              typename FactoryMap::iterator it = _factory.find(id);

              if (it == _factory.end())

                       _factory[id] = p;

              else

                       delete p;

    }

};

 

Что существенного добавилось по сравнению с примером Foo?

Конечно же это метод registerClass, который в качестве параметра принимает объект типа AbstractFactory, который, в свою очередь, умеет создавать объекты того класса, который мы указываем как шаблонный параметр для метода add.

 

Добавляем возможность задания поведения фабрики

 

Чтобы уметь задавать поведение фабрики нам нужно использовать другой паттерн проектирования — Стратегия, так же известного под именем Политика.

Данный паттерн очень хорошо освещен в книге                

А. Александреску «Современное проектирование на С++».

 

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

Теперь встроим нашу политику в фабрику. Это можно сделать несколькими способами — просто наследоваться от класса политики, или использовать класс политики как атрибут фабрики.

В обоих случаях единственная проблема в том, каким образом из класса фабрики задавать шаблонные параметры для класса политики.

В этом нам поможет замечательная особенность С++ — шаблонные параметры. Когда при объявлении класса фабрики указываем что мы хотим передать в качестве параметра не просто какой-то класс, а шаблонный класс.

Но все это очень трудно.

Class Factory

{

public:

virtual Bas_per* createPer() = 0;

virtual ~Factory() {}

};

Int main()

{

P1Factory* person1_factory = new P1Factory;

P2Factory* person2_factory = new P2Factory;

P2Factory* person3_factory = new P3Factory;

vector<Bas_per*> v;

v.push_back(person1_factory->createPer());

v.push_back(person2_factory->createPer());

v.push_back(person_factory->createPer());

for(int i=0; i<v.size(); i++)

v[i]->info();

//...

}

Одиночка (Singleton)

Описание паттерна Singleton

 

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

 

- Такая переменная доступна всегда. Время жизни глобальной переменной - от запуска программы до ее завершения.

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

- Однако, использовать глобальную переменную некоторого типа непосредственно невозможно, так как существует проблема обеспечения единственности экземпляра, а именно, возможно создание нескольких переменных того же самого типа (например, стековых).

 

Class Singleton

{

private:

static Singleton * p_instance;

// другие данные

// Конструкторы и оператор присваивания недоступныклиентам

Singleton() {}

Singleton(const Singleton&);

Singleton& operator=(Singleton&);

 

 public:

static Singleton * getInstance() {

   if(!p_instance)     

       p_instance = new Singleton();

   return p_instance;

}

// другие методы

};

// Singleton.cpp

#include "Singleton.h"

Singleton* Singleton::p_instance = 0;

    - Конструктор объявлен приватным, то есть его можно вызвать только из методов класса, в том числе из статических до создания объектов класса.

    - То есть, чтобы создать объект класса мы не создаем его экземпляр, а «запрашиваем» его.

- Вызывая статический метод Singleton:: getInstance (), вы получите экземпляр, готовый к работе.

- Может оказаться, что к этому моменту уже есть экземпляр, и если его нет, то только в этом случае



Поделиться:


Последнее изменение этой страницы: 2021-11-27; просмотров: 82; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.142.174.55 (0.522 с.)