|
Программирование >> Разработка устойчивых систем
Абстрактные фабрики Паттерн Абстрактная фабрика (Abstract factory) напоминает фабрики, упоминавшиеся ранее, но с несколькими Фабричными методами. Каждый Фабричный метод создает отдельную разновидность объектов. Создавая объект-фабрику, вы решаете, как будут использоваться все объекты, создаваемые этой фабрикой. Пример в книге БЧ обеспечивает переносимость программы между разными графическими пользовательскими интерфейсами (GUI): вы создаете объект-фабрику, соответствующий текущему графическому интерфейсу, а затем при запросе меню, кнопки, ползунка и т. д. фабрика автоматически создает соответствующую версию указанного элемента. Таким образом, все последствия от перехода на другую версию графической среды изолируются в одном месте. Или другой пример: допустим, вы создаете обобщенную игровую среду для разных типов игр. Вот как может выглядеть такая среда при использовании Абстрактной фабрики: ; C10:AbstractFactory.cpp Игровая среда, linclude <iostream> using namespace std; class Obstacle { public: virtual void actionО = 0: class Player { public: virtual void interactWithCObstacle*) = 0; class Kitty: public Player { virtual void interactWith(Obstacle* ob) { cout Kitty has encountered a : ob->action(); class KungFuGuy: public Player { virtual void interactWithCObstacle* ob) { cout KungFuGuy now battles against a ; ob->action(); class Puzzle: public Obstacle { public: void actionO { cout Puzzle endl: } class NastyWeapon: public Obstacle { public: void actionO { cout NastyWeapon endl: } Абстрактная фабрика: class GameElementFactory { Конкретные фабрики: class KittiesAndPuzzles : public GameElementFactory { public: virtual Player* makePlayerO { return new Kitty: } virtual Obstacle* makeObstacleO { return new Puzzle: } class KillAndDismember : public GameElementFactory { public: virtual Player* makePlayerO { return new KungFuGuy: } virtual Obstacle* makeObstacleO { return new NastyWeapon: class GameEnvironment ( GameElementFactory* gef: Player* p: Obstacle* ob: public: GameEnvironment(GameElementFactory* factory) : gef(factory). p(factory->makePlayer()). ob(factory->makeObstacle()) {} void playO { p->interactWith(ob): } ~GameEnvironment() { delete p: delete ob: delete gef: int mainO { GameEnvironment gKnew KittiesAndPuzzles). g2(new KillAndDismember): gl.playO: g2.play(): Kitty has encountered a Puzzle KungFuGuy now battles against a NastyWeapon */ /:- В этой среде объекты Player взаимодействуют с объектами Obstacle, но типы этих объектов зависят от игры. Вы определяете тип игры, выбирая определенный объект GameElementFactory, после чего класс GameEnvironment управляет исходным состоянием и ходом игры. В нашем примере этот класс не рассчитан на наследование, хотя, возможно, это имело бы смысл. Приведенный пример также демонстрирует методику двойной диспетчеризации, которая будет рассматриваться далее. Виртуальные конструкторы Одна из главных целей использования фабрик - такая организация кода, при которой бы вам не приходилось выбирать конкретный тип конструктора при созда- public: virtual Player* makePlayerO = 0: virtual Obstacle* makeObstacle() = 0: Advanced С++ Programming Styles and ldioms , Addison Wesley, 1992. НИИ объекта. Другими словами, фабрике можно сказать: Я не знаю точно, какой объект мне нужен, но вот тебе информация. Со.здай соответствующий тип . Однако при вызове конструктора механизм виртуального вызова не работает (происходит раннее связывание). Иногда это бывает неудобно. Например, в программе Shape было бы вполне логично выполнить всю подготовку в конструкторе Shape, азатем вывести фигуру вызовом draw(). Функция draw() должна быть виртуальной функцией, то есть сообщением классу Shape, приказывающим выполнить рисование конкретного типа фигуры, будь то Circle, Square или Line. Однако с конструктором такой вариант не проходит, потому что при вызове в конструкторах виртуальные функции заменяются локальными телами функций. Если вы хотите, чтобы виртуальная функция вызывалась из конструктора и делала то, что требуется, придется использовать методику имитации виртуального конструктора. Возникает крайне запутанная ситуация. Вспомните: виртуальные функции создаются для того, чтобы вы могли отправить сообщение объекту и дать ему возможность выбрать правильное действие. Но конструктор предназначен для построения объектов, поэтому вызов виртуального конструктора означает следующее: Я не знаю, к какому типу объекта ты относишься, но все равно nocTpoii мне правильный тип . В обычном конструкторе компилятор должен знать адрес таблицы VTABLE, связываемый с VPTR, а виртуальный конструктор (даже если бы он существовал) сделать этого не смог бы, потому что он не располагает всей информацией о типе на стадии компиляции. Невозможность создания виртуальных конструкторов оправданна, потому что это единственная функция, которая должна знать о типе объекта абсолютно все. И все же встречаются ситуации, когда в программе нужно сделать нечто, приближенное к поведению виртуального конструктора. Так, в примере с классом Shape нам хотелось бы передать конструктору Shape уточняющую информацию в списке аргументов и поручить создать конкретную разновидность объекта Shape (Circle или Square) без нашего дальне11Н1его вмешательства. Вместо этого обычно приходится явно вызывать конструктор Circle или Square. У Джеймса Коплина (James О. Coplien) решение этой задачи называе гея конвертом . Под конвертом понимается базовый класс, содержащий указатель па объект, также относящийся к типу базового класса. Конструктор конверта определяет (при вызове на стадии выполнения, а не па стадии компиляции, когда обычно производится проверка типов), какой тип нужно создать, создает объект конкретного типа (в куче) и устанавливает на него ceoii указатель. Далее все вызовы функций производятся базовым классом через указатель. В действительности это всего лишь разновидность паттерна Состояние, где базовый класс работает как суррогатный для производного класса, а производный класс обеспечивает изменения в поведении. : СЮ: Virtual Constructor, срр linclude <iostream> linclude <string> linclude <stdexcept> linclude <stdexcept> linclude <cstddef>
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |