Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 [ 74 ] 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105



SillySoldier

BadSoldier

SuperMonster

SillySuperMonster

BadSuperMonster

Monster

SillyMonster

BadMonster

Рис. 9.1. Иерархия классов для игры с двумя уровнями сложности

Стоит напомнить, что в вашей игре конкретизации классов BadSoldier и Silly-Monster никогда не "встречаются" друг с другом. Это не имело бы смысла. Игрок на легком уровне имеет дело с классами SillySoldier, SillyMonster и SillySuperon-ster, а на уровне повышенной сложности- с классами BadSoldier, BadMonster и BadSuperMonster.

Две категории типов формируют два семейства. Во время игры всегда используются объекты лишь одного конкретного семейства, а комбинации объектов, принадле-жаших разным семействам, никогда не возникают.

Было бы прекрасно, если бы это соответствие выполнялось автоматически. В противном случае, если программист не проявил Необходимой осторожности, новичок, развлекаюшийся с объектами класса SillySoldier, может неожиданно встретиться в темном углу с объектом класса BadMonster. В этом случае обиженные ифоки могут потребовать от вас возврата денег.

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



class AbstractEnemyFactory {

public:

virtual Soldier* MakeSo1dier() = 0; virtual Monster* MakeMonsterQ = 0; virtual SuperMonster* MakeSuperMonsterO = 0;

Тогда для каждого уровня сложности создается конкретная фабрика врагов, создающая объекты в соответствии с заданной стратегией ифы.

class EasyLevelEnemyFactory : public AbstractEnemyFactory

public:

Soldier* MakeSoldier;

{ return new SillySoldier; }

Monster* MakeMonsterO

{ return new SillyMonster; }

SuperMonster* MakeSuperMonsterO

{ return new sillySuperMonster; }

class DieHardLevelEnemyFactory : public AbstractEnemyFactory {

publi с:

Soldier* MakeSoldierO

{ return new BadSoldier; }

Monster* makeMonsterO

{ return new BadMonster; }

SuperMonster* MakeSuperMonsterO

{ return new BadSuperMonster; }

в заключение мы инициализируем указатель на объект класса AbstractEnemyFactory соответствующего конкретного класса.

class GameApp {

void SelectLevelO {

i f (.пользователь выбрал легкий уровень игры) {

pFactory = new EasyLevelEnemyFactory;

else {

pFactory = new DieHardLevelEnemyFactory;

private:

используем указатель pFactory для создания врагов AbstractEnemyFactory* pFactory ;

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

Шаблон проектирования Abstract Factory предписывает, чтобы все функции, предназначенные для создания объектов, были сосредоточены в отдельном интерфей-



се. Затем профаммист должен выполнить реализацию этого интерфейса отдельно для каждого семейства создаваемых объектов.

Типы, анонсированные абсфактным фабричным интерфейсом (Soldier, Monster и SuperMonster), называются абстрактными изделиями (abstract product). Типы, которые фактически создаются (SillySoldier, BadSoldier, SillyMonster и т.д.), называются конкретными изделиями (concrete product). Эти термины уже встречались в главе 8.

Основной недостаток шаблона Abstract Factory заключается в том, что базовый класс фабрики (в нашем примере AbstractEnemyFactory) должен владеть информацией о каждом создаваемом абсфактном изделии. Кроме того, по крайней мере в только что описанной реализации, каждый конкретный фабричный класс зависит от конкретного изделия, которое он создает.

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

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

Как это часто бывает, правильное решение можно найти с помошью компромисса. В каждом конкретном приложении следует искать свою золотую середину. Обшее правило таково; используйте статическую модель, если можете, и динамическую модель, если вынуждены.

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

9.2. Обобщенный интерфейс шаблона Abstract Factory

Как указывалось в главе 3, функгхиональные возможности класса Typelist позволяют реализовать обобшенный шаблон Abstract Factory. В этом разделе показано, как определяется обобшенный интерфейс AbstractFactory с помошью списков типов.

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

• Определяется абсфактный класс (класс абсфактной фабрики), содержащий одну чисто виртуальную функцию для каждого типа изделий. Виртуальная функция, соответствующая типу т, обычно возврашает указатель типа т*. Она называется createT, макет или как-то еше.

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

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



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 [ 74 ] 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105