Программирование >>  Разработка устойчивых систем 

1 ... 164 165 166 [ 167 ] 168 169 170 ... 196


Рассматривается в книге БЧ при описании эталона Посетитель, о котором речь далее в этом разделе.

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

В основу решения заложена методика, называемая множественной диспетче-ризациейК В нашем примере решение принимается по двум составляющим, что называется двойной диспетчеризацией. Помните, что полиморфизм работает только при вызове виртуальных функций, поэтому если вы хотите организовать множественную диспетчеризацию, в механизм необходимо включить вызов виртуальной функции для определения каждого неизвестного типа. Таким образом, при организации взаимодействия между двумя разными иерархиями типов в каждой иерархии должен присутствовать виртуальный вызов. Обычно используется конфигурация, при которой один вызов функции класса генерирует несколько вызовов виртуальных функций и таким образом определяет сразу несколько типов. В следующем примере вызываются виртуальные функции compete() и eval(), причем обе функции относятся к одному типу (что не является обязательным требованием при множественной диспетчеризации):

: C10:PaperScissorsRock.cpp

Множественная диспетчеризация.

linclude <algorithm>

linclude <iostream>

linclude <iterator>

linclude <vector>

linclude <ctime>

linclude <cstdlib>

linclude ../purge.h

using namespace std;

class Paper; class Scissors; class Rock;

enum Outcome { WIN. LOSE, DRAW };

ostreamS operator (ostream& os, const Outcome out) { switch(out) { default:

case WIN: return os win ; case LOSE: return os lose ; case DRAW: return os draw ;

class Item { public:

virtual Outcome compete(const Item*) = 0: virtual Outcome eval(const Paper*) const = 0; virtual Outcome eval(const Scissors*) const= 0; virtual Outcome eval(const Rock*) const = 0: virtual ostreamS print(ostreamS os) const = 0: virtual -ItemO {}

friend ostreamS operator (ostreamS os. const Item* it) {



class Paper : public Item { public:

Outcome competeCconst Item* it) { return it->eval(this):} Outcome eval(const Paper*) const { return DRAW: } Outcome eval(const Scissors*) const { return WIN: } Outcome eval(const Rock*) const { return LOSE: } ostream& print(ostream& os) const { return OS Paper :

class Scissors : public Item { public:

Outcome compete(const Item* it) { return it->eval(this):} Outcome eval(const Paper*) const { return LOSE: } Outcome eval(const Scissors*) const { return DRAW: } Outcome eval(const Rock*) const { return WIN: } ostream& print(ostream& os) const { return OS Scissors :

class Rock : public Item { public:

Outcome competeCconst Item* it) { return it->eval(this):} Outcome eval(const Paper*) const { return WIN: } Outcome eval(const Scissors*) const { return LOSE: } Outcome eval(const Rock*) const { return DRAW: } ostream& print(ostream& os) const { return OS Rock :

struct ItemGen {

Item* operatorOO { switch(rand() % 3) { default:

case 0: return new Scissors:

case 1: return new Paper:

case 2: return new Rock:

struct Compete { Outcome operatorO(Item* a. Item* b) { cout a \t b \t : return a->compete(b):

int mainO {

srand(time(0)): Раскрутка генератора случайных чисел const int sz = 20: vector<Item*> v(sz*2):

return it->print(os):



generate(v.beginO. v.endO. ItemGenO); transform(v.begin(). v.beginO + sz. V.beginO + sz.

ostream iterator<Outcome>(cout. \n ). CompeteO); purge(v): } /:-

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

Item - базовый класс для типов с множественной диспетчеризацией. Операторная функция Compete::operator() получает два объекта Item* (точный тип обоих объектов неизвестен) и начинает процесс двойной диспетчеризации вызовом виртуальной функции Item::compete(). Механизм виртуального вызова определяет тип а, поэтому управление передается функции compete() конкретного типа а. Функция compete() выполняет вторую диспетчеризацию, вызывая eval() для оставшегося типа. Передача текущего объекта (this) в аргументе eval() порождает вызов перегруженной функции eval() с сохранением информации о типе первой диспетчеризации. При заверщении второй диспетчеризации точные типы обоих объектов Item становятся известными.

В функции main() алгоритм generate() библиотеки STL заполняет вектор v, а затем transformO применяет операторную функцию Compete::operator() к двум интервалам. Данная версия transform() получает начальную и конечную точки первого интервала (с левосторонними объектами Item, используемыми при двойной диспетчеризации); начальную точку второго интервала с правосторонними объектами Item; итератор приемника, которым в данном случае является стандартный выходной поток; и объект функции (временный объект типа Compete), вызываемый для каждого объекта.

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

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

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

В паттерне Посетитель операции выделяются из иерархии классов в отдельную внешнюю иерархию. Основная иерархия содержит функцию visit(), прини-



1 ... 164 165 166 [ 167 ] 168 169 170 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика