Паттерны проектирования (шаблоны проектирования)

Реферат

Паттерны проектирования (иначе шаблоны проектирования).

Ранее мы рассматривали вопросы, связанные с программированием с точки зрения программиста, далее коснемся этих вопросов со стороны проектировщика программных систем.

Паттерны проектирования предназначены для:

  • эффективного решения характерных задач проектирования;

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

  • указания отношения и взаимодействия между классами и объектами.

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

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

Что положительное несет использование паттернов при проектировании программных систем.

  • Каждый паттерн описывает решение целого класса проблем..

  • Каждый паттерн имеет известное имя.

  • Шаблоны проектирования не зависят от языка программирования (объектно-ориентированного), в отличие от идиом.

Идиома (программирование) — низкоуровневый шаблон проектирования, характерный для конкретного языка программирования.

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

Порождающие паттерны проектирования

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

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

186 стр., 92891 слов

Разработка и проектирование ресторана первого класса на 70 посадочных ...

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

Использование

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

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

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

Перечень порождающих паттернов

К порождающим паттернам проектирования относятся следующие:

  • фабрика (abstract factory);

  • (builder);

  • фабричный метод (factory method);

  • прототип (prototype);

  • одиночка (singleton)

Абстрактная фабрика, Абстрактная фабрика, Назначение

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

Достоинства

  • изолирует конкретные классы;

  • упрощает замену семейств продуктов;

  • гарантирует сочетаемость продуктов.

Недостатки, Применение

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

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

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

  • Требуется предоставить библиотеку объектов, раскрывая только их интерфейсы, но не реализацию.

    8 стр., 3890 слов

    Проектирование информационных систем

    ... включает в себя информацию о следующих компонентах: клиентах заказах ассортименте работ стоимости работ сотрудниках расходных материалах поставщиках Подразумевается что вся информация будет храниться ... вырабатываются рекомендации по выполнению следующих заказов. Разрабатываемая ИС должна позволять вести обработку, учет и контроль информации по работе предприятия. Обработка – добавление, удаление и ...

Недостатки 1

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

Пример

#include <iostream>

//

class Car

{ public:

  • virtual void info() = 0;

virtual ~Car(){}

};

// Конкретный ProductA1

class Ford : public Car

{ public:

virtual void info()

{ std::cout << «Ford» << std::endl; }

};

// Конкретный ProductA2

class Toyota : public Car

{ public:

virtual void info()

{ std::cout << «Toyota» << std::endl; }

};

//

class Engine

{ public:

  • virtual void getPower() = 0;

virtual ~Engine(){}

};

// Конкретный ProductB1

class FordEngine : public Engine

{ public:

virtual void getPower()

{ std::cout << «Ford Engine 4.4» << std::endl; }

};

// Конкретный ProductB2

class ToyotaEngine : public Engine

{ public:

virtual void getPower()

{ std::cout << «Toyota Engine 3.2» << std::endl; }

};

// AbstractFactory

class CarFactory

{ public:

Car* getNewCar()

{ return createCar(); }

Engine* getNewEngine()

{ return createEngine(); }

virtual ~ CarFactory(){}

protected:

  • virtual Car* createCar() = 0;
  • virtual Engine* createEngine() = 0;

};

// Конкретная Factory1

class FordFactory : public CarFactory

{ protected:

// from CarFactory

virtual Car* createCar()

{ return new Ford(); }

virtual Engine* createEngine()

{ return new FordEngine(); }

};

// Конкретная Factory2

class ToyotaFactory : public CarFactory

{ protected:

// from CarFactory

virtual Car* createCar()

{ return new Toyota(); }

virtual Engine* createEngine()

{ return new ToyotaEngine(); }

};

int main()

{

CarFactory* curFactory = NULL;

  • Car* myCar = NULL;
  • Engine* myEngine = NULL;
  • ToyotaFactory toyotaFactory;
  • FordFactory fordFactory;
  • curFactory = &toyotaFactory;
  • myCar = curFactory->getNewCar();
  • myCar->info();
  • myEngine = curFactory->getNewEngine();
  • myEngine->getPower();
  • delete myCar;
  • delete myEngine;
  • curFactory = &fordFactory;
  • myCar = curFactory->getNewCar();
  • myCar->info();
  • myEngine = curFactory->getNewEngine();
  • myEngine->getPower();
  • delete myCar;
  • delete myEngine;
  • return 0;

}

Недостатки 2

Назначение

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

Недостатки

  • позволяет изменять внутреннее представление продукта;

  • изолирует код, реализующий конструирование и представление;

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

Применение

  • алгоритм создания сложного объекта не должен зависеть от того, из каких частей состоит объект и как они стыкуются между собой;

Применение 1

Пример

#include <iostream>

  • #include <memory>
  • #include <string>

// Product

class Pizza

{

private:

  • std::string dough; //

std::string sauce; // соус

std::string topping; //

public:

Pizza() { }

~Pizza() { }

void SetDough(const std::string& d) { dough = d; };

  • void SetSauce(const std::string& s) { sauce = s;
  • };
  • void SetTopping(const std::string& t) { topping = t; }

void ShowPizza()

{

std::cout << » Yummy !!!» << std::endl

<< «Pizza with Dough as » << dough

<< «, Sauce as » << sauce

<< » and Topping as » << topping

<< » !!! » << std::endl;

}

};

// Abstract Builder

class PizzaBuilder

{

protected:

  • std::auto_ptr<Pizza>
  • pizza;

public:

PizzaBuilder() {}

virtual ~PizzaBuilder() {}

std::auto_ptr<Pizza> GetPizza() { return pizza; }

void createNewPizzaProduct() { pizza.reset (new Pizza); }

virtual void buildDough()=0;

  • virtual void buildSauce()=0;
  • virtual void buildTopping()=0;

};

// ConcreteBuilder

class HawaiianPizzaBuilder : public PizzaBuilder

{

public:

HawaiianPizzaBuilder() : PizzaBuilder() {}

~HawaiianPizzaBuilder(){}

void buildDough() { pizza->SetDough(«cross»); }

void buildSauce() { pizza->SetSauce(«mild»); }

void buildTopping() { pizza->SetTopping(«ham and pineapple»); }

};

// ConcreteBuilder

class SpicyPizzaBuilder : public PizzaBuilder

{

public:

SpicyPizzaBuilder() : PizzaBuilder() {}

~SpicyPizzaBuilder() {}

void buildDough() { pizza->SetDough(«pan baked»); }

void buildSauce() { pizza->SetSauce(«hot»); }

void buildTopping() { pizza->SetTopping(«pepperoni and salami»); }

};

// Director

class Waiter

{

private:

  • PizzaBuilder* pizzaBuilder;

public:

Waiter() : pizzaBuilder(NULL) {}

~Waiter() { }

void SetPizzaBuilder(PizzaBuilder* b) { pizzaBuilder = b; }

std::auto_ptr<Pizza> GetPizza() { return pizzaBuilder->GetPizza(); }

void ConstructPizza()

{

pizzaBuilder->createNewPizzaProduct();

  • pizzaBuilder->buildDough();
  • pizzaBuilder->buildSauce();
  • pizzaBuilder->buildTopping();

}

};

  • // Клиент заказывает две пиццы.

int main()

{

Waiter waiter;

  • HawaiianPizzaBuilder hawaiianPizzaBuilder;
  • waiter.SetPizzaBuilder (&hawaiianPizzaBuilder);
  • waiter.ConstructPizza();
  • std::auto_ptr<Pizza>
  • pizza = waiter.GetPizza();
  • pizza->ShowPizza();
  • SpicyPizzaBuilder spicyPizzaBuilder;
  • waiter.SetPizzaBuilder(&spicyPizzaBuilder);
  • waiter.ConstructPizza();
  • pizza = waiter.GetPizza();
  • pizza->ShowPizza();
  • return EXIT_SUCCESS;

}

Применение 2

Фабричный метод, Фабричный метод

Назначение

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

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

  • класс спроектирован так, чтобы объекты, которые он создаёт, специфицировались подклассами.

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

Назначение 1

Достоинства

  • позволяет сделать код создания объектов более универсальным, не привязываясь к конкретным классам (ConcreteProduct), а оперируя лишь общим интерфейсом (Product);

  • позволяет установить связь между параллельными иерархиями классов.

Недостатки

Пример

#include<iostream>

  • #include<string>
  • using namespace std;

// Product

class Product

{ public:

  • virtual string getName() = 0;
  • virtual ~Product(){};

// ConcreteProductA

class ConcreteProductA : public Product

{ public:

string getName()

{ return «ConcreteProductA»; }

};

// ConcreteProductB

class ConcreteProductB : public Product

{ public:

string getName()

{ return «ConcreteProductB»; }

};

// Creator

class Creator

{ public:

  • virtual Product* FactoryMethod() = 0;
  • virtual ~ Creator (){};

};

// ConcreteCreatorA

class ConcreteCreatorA : public Creator

{ public:

Product* FactoryMethod()

{ return new ConcreteProductA(); }

};

// ConcreteCreatorB

class ConcreteCreatorB : public Creator

{ public:

Product* FactoryMethod()

{ return new ConcreteProductB(); }

};

int main()

{ const int size = 2;

// Создание массива конкретных creators

Creator* creators[size];

  • creators[0] = new ConcreteCreatorA();
  • creators[1] = new ConcreteCreatorB();

// Для каждого из creators создание своего product

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

{ Product* product = creators[i]->FactoryMethod();

  • cout<<product->getName()<<endl;
  • delete product;

}

for(int i=0; i<size; i++) delete creators[i];

  • return 0;

}

Недостатки 1

Прототип

Назначение

Задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копирования этого прототипа…

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

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

  • инстанцируемые классы определяются во время выполнения, например с помощью динамической загрузки;

  • для того чтобы избежать построения иерархий классов или фабрик, параллельных иерархии классов продуктов;

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

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

Пример

Одиночка

Цель

экземпляром

Достоинства

  • контролируемый доступ к единственному экземпляру;

  • уменьшение числа имён;

  • допускает уточнение операций и представления;

  • допускает переменное число экземпляров;

  • большая гибкость, чем у операций класса.

Недостатки

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

Применение

  • должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам;

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

Применение 1

Пример

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

template<typename T> class Singleton

{

public:

static T& Instance()

{

static T theSingleInstance; // у класса T есть конструктор по

// умолчанию

return theSingleInstance;

}

};

  • class OnlyOne : public Singleton<OnlyOne>

{

//.. интерфейс класса

};

Применение 2

Синглтон на диаграмме классов

=====================================================

Адаптер (шаблон проектирования)

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

Основные характеристики

Задача

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

Способ решения

Адаптер предусматривает создание класса-оболочки [1] с требуемым интерфейсом.

Участники

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

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

Следствия

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

Реализация

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