Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Design Patterns via C#.pdf
Скачиваний:
154
Добавлен:
17.03.2016
Размер:
13.25 Mб
Скачать

282

На представленной ниже диаграмме взаимодействия показаны отношения между объектами: клиентом (Program), объектной структурой (ObjectStructure), посетителем (ConcreteVisitor) и двумя элементами (ConcreteElementA и ConcreteElementB).

См. Пример к главе: \023_Visitor\001_Visitor

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

Паттерн Visitor рекомендуется использовать, когда:

В (гетерогенной) коллекции (ObjectStructure) должны присутствовать разнотипные объекты (ConcreteElementA и ConcreteElementB) с разнородными интерфейсами и при этом требуется организовать унифицированный обход элементов этой коллекции и выполнить определенные операции над каждым имеющимся объектом.

283

class ObjectStructure { // Подчеркиваетсяя гетерогенность. ArrayList elements = new ArrayList();

public void Add(Element element) { elements.Add(element); }

public void Remove(Element element) { elements.Remove(element); }

public void Accept(Visitor visitor) { foreach (Element element in elements) element.Accept(visitor); } }

Над объектами (ConcreteElementA и ConcreteElementB) входящими в состав коллекции (ObjectStructure) требуется выполнять определенные операции и при этом не хотелось бы повторять эти операции в каждом классе (ConcreteElementA и ConcreteElementB). Код этих операций можно вынести в методы объекта посетителя класса ConcreteVisitor.

class ConcreteVisitor : Visitor

{

public override void VisitElementA(ElementA elementA)

{

//Код который мог быть размещен в классе ElementA,

//расширяет собой класс ElementA. elementA.SomeState = "State A"; Console.WriteLine(elementA.SomeState);

//Работа с разнородным интерфейсом. elementA.OperationA();

}

public override void VisitElementB(ElementB elementB)

{

//Код который мог быть размещен в классе ElementB,

//расширяет собой класс ElementB. elementB.SomeState = "State B"; Console.WriteLine(elementB.SomeState);

//Работа с разнородным интерфейсом. elementB.OperationB();

}

}

Классы объектов (ConcreteElementA и ConcreteElementB), входящих в коллекцию (ObjectStructure) изменяются редко, но новые операции, производимые над коллекцией требуется добавлять часто. Важно понимать, что при изменении интерфейса классов объектов (ConcreteElementA и ConcreteElementB), входящих в состав коллекции, вероятней всего потребуется переопределить и интерфейсы всех объектов посетителей (ConcreteVisitor1 и

ConcreteVisitor2), а это может быть затруднительно. Поэтому, если классы (ConcreteElementA и ConcreteElementB) изменяются часто, то лучше все операции определять прямо в них (т.е., не выносить операции в посетителей).

284

Пример отображающий идеи применимости паттерна Visitor, рассмотренные выше.

class Program

{

static void Main()

{

ObjectStructure structure = new ObjectStructure();

structure.Add(new ElementA()); structure.Add(new ElementB());

structure.Accept(new ConcreteVisitor());

}

}

 

abstract class Visitor

 

{

 

public abstract void VisitElementA(ElementA elementA);

 

public abstract void VisitElementB(ElementB elementB);

class ObjectStructure

}

{

 

// Подчеркиваетсяя гетерогенность.

 

ArrayList elements = new ArrayList();

 

public void Add(Element element)

class ConcreteVisitor : Visitor

{

{

public override void VisitElementA(ElementA elementA)

elements.Add(element);

{

}

// Код который мог быть размещен в классе ElementA,

 

public void Remove(Element element)

// расширяет собой класс ElementA.

elementA.SomeState = "State A";

{

Console.WriteLine(elementA.SomeState);

elements.Remove(element);

 

}

// Работа с разнородным интерфейсом.

 

public void Accept(Visitor visitor)

elementA.OperationA();

}

{

 

foreach (Element element in elements)

public override void VisitElementB(ElementB elementB)

element.Accept(visitor);

{

}

// Код который мог быть размещен в классе ElementB,

}

// расширяет собой класс ElementB.

 

 

elementB.SomeState = "State B";

 

Console.WriteLine(elementB.SomeState);

 

// Работа с разнородным интерфейсом.

 

elementB.OperationB();

 

}

 

}

abstract class Element

{

public abstract void Accept(Visitor visitor); public string SomeState { get; set; }

}

class ElementA : Element

class ElementB : Element

{

{

public override void Accept(Visitor visitor)

public override void Accept(Visitor visitor)

{

{

visitor.VisitElementA(this);

visitor.VisitElementB(this);

}

}

public void OperationA()

public void OperationB()

{

{

Console.WriteLine("OperationA");

Console.WriteLine("OperationB");

}

}

}

}

См. Пример к главе: \023_Visitor\003_Visitor

285

Результаты

Паттерн Visitor обладает следующими преимуществами:

Упрощение добавления новых методов.

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

Объединение сходного поведения.

Сходное поведение (функциональность) относящееся к объектам-элементам, не разносится по всем классам (ConcreteElement), а локализуется (размещается) в классе объекта-посетителя. Не связанная друг с другом функциональность распределяется по отдельным конкретным классам (ConcreteVisitor). Такой подход позволяет упростить как сами классы элементов (ConcreteElement), так и алгоритм, располагающийся внутри посетителя (все данные которые относятся к алгоритму можно располагать непосредственно в посетителе, т.е. рядом с алгоритмом).

Накопление состояния.

Посетитель может накапливать в себе информацию о состоянии элементов, входящих в объектную структуру. Если не использовать паттерн Visitor, то состояние придется передавать в виде дополнительных (ref/out) параметров методов, выполняющих обход и сохранять в специально отведенном месте, что может вызвать определенные неудобства.

Паттерн Visitor обладает следующими недостатками:

Сложность добавления новых классов ConcreteElement.

При использовании паттерна Visitor, возникают некоторые сложности при добавлении новых классов элементов (ConcreteElement). Связано это с тем, что создание нового класса ConcreteElement, требует добавления нового абстрактного метода VisitConcretElementX в абстрактный класс Visitor, этот абстрактный метод потребуется реализовать во всех производных классах ConcreteVisitor.

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

Разрушение инкапсуляции.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]