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


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



ЗНАЕТЕ ЛИ ВЫ?

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



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

Дизайн шаблона Абстрактная фабрика включает большое число участников, однако он достаточно прост (рис. 13). Клиент хранит ссылку на конкретную фабрику, реализующую интерфейс IFactory. Диаграмма показывает, что один из вариантов установки связи клиента и фабрики – это передача объекта-фабрики в конструкторе клиента. Клиент работает с определённым набором объектов, но использует для этого только реализуемые классами объектов интерфейсы (IProductA и IProductB). Для получения нужного объекта клиент вызывает один из методов фабрики.

 

Рис. 13. Шаблон абстрактная фабрика.

Рассмотрим пример кода с реализацией шаблона абстрактная фабрика. Отметим одну важную отличительную особенность представленной реализации шаблона. Вместо набора классов-фабрик и классов для объектов клиента используются универсальные шаблоны (generics). Конкретный класс-фабрика получается конструированием из общего универсального шаблона при помощи параметра TBrand.

// Общий интерфейс фабрики

public interface IFactory

{

IBag CreateBag();

IShoes CreateShoes();

}

 

// Это generic-класс будет конструироваться в конкретные фабрики

public class Factory<TBrand>: IFactory where TBrand: IBrand, new()

{

public IBag CreateBag()

{

return new Bag<TBrand>();

}

 

public IShoes CreateShoes()

{

return new Shoes<TBrand>();

}

}

 

// Два интрефейса, с которыми работает клиент

public interface IBag

{

string Material { get; }

}

 

public interface IShoes

{

int Price { get; }

}

 

// Generic-классы для конкретных классов-продуктов

public class Bag<TBrand>: IBag where TBrand: IBrand, new()

{

private readonly TBrand myBrand;

 

public Bag()

{

myBrand = new TBrand();

}

 

public string Material

{

get { return myBrand.Material; }

}

}

 

public class Shoes<TBrand>: IShoes where TBrand: IBrand, new()

{

private readonly TBrand myBrand;

 

public Shoes()

{

myBrand = new TBrand();

}

 

public int Price

{

get { return myBrand.Price; }

}

}

 

// Интерфейс для описания брэндов и конкретные брэнды

public interface IBrand

{

int Price { get; }

string Material { get; }

}

 

public class Gucci: IBrand

{

public int Price

{

get { return 1000; }

}

 

public string Material

{

get { return "Crocodile skin"; }

}

}

 

public class Poochy: IBrand

{

public int Price

{

get { return new Gucci().Price / 3; }

}

 

public string Material

{

get { return "Plastic"; }

}

}

 

public class Client<TBrand> where TBrand: IBrand, new()

{

public void ClientMain()

{

IFactory factory = new Factory<TBrand>();

var bag = factory.CreateBag();

var shoes = factory.CreateShoes();

Console.WriteLine("A Bag is made from " + bag.Material);

Console.WriteLine("Shoes’ cost is" + shoes.Price);

}

}

 

public static class AbstractFactoryExample

{

private static void Main()

{

new Client<Poochy>().ClientMain();

new Client<Gucci>().ClientMain();

}

}

Строитель (Builder)

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

Рассмотрим дизайн шаблона Строитель. В его основе лежит компонент Director (режиссер), который вызывает объекты-строители. Количество объектов-строителей произвольно, однако все они реализуют интерфейс IBuilder. Строители поставляют элементы для создания финального объекта Product. Как только объект Product будет создан, компонент Director предоставляет его клиенту.

 

Рис. 14. Дизайн шаблона Строитель.

Пример кода с реализацией шаблона Строитель основан на примере для шаблона Абстрактная фабрика.

using System;

using System.Threading;

 

public interface IBuilder

{

IBag CreateBag();

}

 

public class Builder<TBrand>: IBuilder where TBrand: IBrand, new()

{

private readonly TBrand myBrand = new TBrand();

 

public IBag CreateBag()

{

return myBrand.CreateBag();

}

}

 

public interface IBag

{

string Properties { get; set; }

}

 

public class Bag: IBag

{

public string Properties { get; set; }

}

 

public interface IBrand

{

IBag CreateBag();

}

 

public class Gucci: IBrand

{

public IBag CreateBag()

{

var b = new Bag();

BuilderExample.DoWork("Cut Leather", 250);

BuilderExample.DoWork("Sew leather", 1000);

b.Properties += "Leather";

BuilderExample.DoWork("Create Lining", 500);

BuilderExample.DoWork("Attach Lining", 1000);

b.Properties += " lined";

BuilderExample.DoWork("Add Label", 250);

b.Properties += " with label";

return b;

}

}

 

public class Poochy: IBrand

{

public IBag CreateBag()

{

var b = new Bag();

BuilderExample.DoWork("Hire cheap labour", 200);

BuilderExample.DoWork("Cut Plastic", 125);

BuilderExample.DoWork("Sew Plastic", 500);

b.Properties += "Plastic";

BuilderExample.DoWork("Add Label", 100);

b.Properties += " with label";

return b;

}

}

 

public class Client<TBrand> where TBrand: IBrand, new()

{

public void ClientMain()

{

IBuilder factory = new Builder<TBrand>();

var date = DateTime.Now;

Console.WriteLine("I want to buy a bag!");

var bag = factory.CreateBag();

Console.WriteLine("I got my Bag which took " +

DateTime.Now.Subtract(date).TotalSeconds * 5 + " days");

Console.WriteLine("with the following properties " +

bag.Properties);

}

}

 

public static class BuilderExample

{

private static void Main()

{

new Client<Poochy>().ClientMain();

new Client<Gucci>().ClientMain();

}

 

public static void DoWork(string workitem, int time)

{

Console.Write("{0}: 0%", workitem);

Thread.Sleep(time);

Console.Write("....25%");

Thread.Sleep(time);

Console.Write("....50%");

Thread.Sleep(time);

Console.WriteLine("....100%");

}

}

3.8. ШАБЛОНЫ ПОВЕДЕНИЯ: СТРАТЕГИЯ, СОСТОЯНИЕ, ШАБЛОННЫЙ МЕТОД

Стратегия (Strategy)

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

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

 

Рис. 15. UML-диаграмма шаблона Стратегия.

Ниже приведён пример кода с реализацией шаблона Стратегия.

using System.Collections.Generic;

 

public interface IHolidaySet

{

List<string> GetHolidays();

}

 

public class USHolidaySet: IHolidaySet

{

public List<string> GetHolidays()

{

return new List<string> { "01.01.09", "25.05.09",

"04.07.09", "07.09.09",

"26.11.09", "25.12.09" };

}

}

 

public class RussianHolidaySet: IHolidaySet

{

public List<string> GetHolidays()

{

return new List<string> { "01.01.09", "07.01.09",

"23.02.09", "08.03.09",

"19.04.09", "12.06.09",

"04.11.09" };

}

}

 

public class Client

{

private readonly IHolidaySet holidaySetStrategy;

 

public Client(IHolidaySet strategy)

{

holidaySetStrategy = strategy;

}

 

public bool CheckForHoliday(string date)

{

return holidaySetStrategy.GetHolidays().Contains(date);

}

}

 

public static class StrategyExample

{

private static void Main()

{

var client = new Client(new USHolidaySet());

var result = client.CheckForHoliday("01.01.09");

}

}

Состояние (State)

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

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

Рассмотрим пример конечного автомата. Опишем поведение родителя, отправившего сына в школу. Сын приносит двойки и пятерки. Отец не хочет хвататься за ремень каждый раз, как только сын получит очередную двойку, и выбирает более тонкую тактику воспитания. Чтобы описать модель поведения отца, используем граф, в котором вершины соответствуют состояниям, а дуга, помеченная х/у, из состояния s в состояние q проводится тогда, когда автомат из состояния s под воздействием входного сигнала х переходит в состояние q с выходной реакцией у. Граф автомата, моделирующего поведение родителя, представлен на рис. 16.

 

Рис. 16. Автомат, описывающий поведение «умного» отца.

Этот автомат имеет четыре состояния s0, s1,s2,s3 и два входных сигнала – оценки, полученные сыном в школе: {2, 5}. Начиная с начального состояния s0 (оно помечено особо), автомат под воздействием входных сигналов переходит из одного состояния в другое и выдаёт выходные сигналы – реакции на входы. Выходы автомата будем интерпретировать как действия родителя так:

● y0 – «брать ремень»;

● y1 – «ругать»;

● y2 – «успокаивать»;

● y3 – «надеяться»;

● y4 – «радоваться»;

● y5 – «ликовать».

При наивной программной реализации конечного автомата порождаются наборы конструкций switch-case, которые, как правило, вложены в друг друга. Использование шаблона Состояние позволяет упростить код. Все состояния конечного автомата описываются отдельными классами, которые обладают набором виртуальных методов, соответствующих входным сигналам. Получение очередного входного сигнала означает вызов метода того объекта, экземпляр которого находится в поле state. При этом сам вызываемый метод может поместить в state другой объект-состояние (переключить состояние).

 

Рис. 17. Дизайн шаблона состояние.

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

public class SmartParent

{

internal AbstractState _state = State0.Instance;

 

public void HandleTwo()

{

_state.HandleTwo(this);

}

 

public void HandleFive()

{

_state.HandleFive(this);

}

}

 

public abstract class AbstractState

{

public abstract void HandleTwo(SmartParent parent);

public abstract void HandleFive(SmartParent parent);

}

 

public class State0: AbstractState

{

private static State0 _instance;

 

public static State0 Instance

{

get { return _instance?? (_instance = new State0()); }

}

 

public override void HandleTwo(SmartParent parent)

{

Console.WriteLine("Успокаивать");

parent._state = State1.Instance;

}

 

public override void HandleFive(SmartParent parent)

{

Console.WriteLine("Радоваться");

parent._state = State3.Instance;

}

}

 

public class State1: AbstractState

{

private static State1 _instance;

 

public static State1 Instance

{

get { return _instance?? (_instance = new State1()); }

}

 

public override void HandleTwo(SmartParent parent)

{

Console.WriteLine("Ругать");

parent._state = State2.Instance;

}

 

public override void HandleFive(SmartParent parent)

{

Console.WriteLine("Надеяться");

parent._state = State0.Instance;

}

}

 

public class State2: AbstractState

{

private static State2 _instance;

 

public static State2 Instance

{

get { return _instance?? (_instance = new State2()); }

}

 

public override void HandleTwo(SmartParent parent)

{

Console.WriteLine("Брать ремень");

parent._state = State2.Instance;

}

 

public override void HandleFive(SmartParent parent)

{

Console.WriteLine("Надеяться");

parent._state = State0.Instance;

}

}

 

public class State3: AbstractState

{

private static State3 _instance;

 

public static State3 Instance

{

get { return _instance?? (_instance = new State3()); }

}

 

public override void HandleTwo(SmartParent parent)

{

Console.WriteLine("Успокаивать");

parent._state = State1.Instance;

}

 

public override void HandleFive(SmartParent parent)

{

Console.WriteLine("Ликовать");

parent._state = State3.Instance;

}

}

Шаблонный метод (Template method)

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

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

using System;

 

public abstract class Sorter

{

private readonly int[] data;

 

protected Sorter(params int[] source)

{

data = new int[source.Length];

Array.Copy(source, data, source.Length);

}

 

protected abstract bool Compare(int x, int y);

 

public void Sort()

{

for (var i = 0; i < data.Length - 1; i++)

{

for (var j = i + 1; j < data.Length; j++)

{

if (Compare(data[j], data[i]))

{

var temp = data[i];

data[i] = data[j];

data[j] = temp;

}

}

}

}

 

public int[] GetData()

{

var result = new int[data.Length];

Array.Copy(data, result, data.Length);

return result;

}

}

 

public class GreaterFirstSorter: Sorter

{

public GreaterFirstSorter(params int[] source): base(source){ }

 

protected override bool Compare(int x, int y)

{

return x > y;

}

}

 

public class TemplateMethodExample

{

private static void Main()

{

var sorter = new GreaterFirstSorter(1, 3, -10, 0);

sorter.Sort();

var result = sorter.GetData();

}

}

3.9. ШАБЛОНЫ ПОВЕДЕНИЯ: ЦЕПОЧКА ОБЯЗАННОСТЕЙ И КОМАНДА



Поделиться:


Последнее изменение этой страницы: 2016-09-13; просмотров: 55; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

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