Реализация паттерна «Стратегия» посредством указателей на функции
Реализация паттерна «Стратегия» посредством указателей на функции
Идиома NVI – это интересная альтернатива открытым виртуальным функциям, но с точки зрения проектирования она дает не слишком много. В конце концов, мы по-прежнему используем виртуальные функции для вычисления жизненной силы каждого персонажа. С точки зрения проектирования гораздо более сильным было бы утверждение о том, что вычисление жизненной силы персонажа не зависит от типа персонажа, что такие вычисления вообще не являются свойством персонажа как такового. Например, мы можем потребовать, чтобы конструктору каждого персонажа передавался указатель на функцию, которая вызывалась бы для вычисления его жизненной силы:
class GameCharacter; // опережающее объявление
// функция алгоритма по умолчанию для вычисления жизненной силы персонажа
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:
typedef int (*HealthCalcFunc)(const GameCharacter&);
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
: healthFunc(hcf)
{}
int healthValue() const
{ return healthFunc(*this);}
...
private:
HealthCalcFunc healthFunc;
};
Это простой пример применения другого распространенного паттерна проектирования – «Стратегия» (Strategy). По сравнению с подходами, основанными на виртуальных функциях в иерархии GameCharacter, он предоставляет некоторые любопытные возможности, повышающие гибкость:
• Разные экземпляры персонажей одного и того же типа могут иметь разные функции вычисления жизненной силы. Например:
class EvilBadGay: public GameCharacter {
public:
explicit EvilBadGay(HealthCalcFunc hcf = defaultHealthCalc)
: GameCharacter(hcf)
{...}
...
};
int loseHealthQuickly(const GameCharacter&); // функции вычисления
int loseHealthSlowly(const GameCharacter&); // жизненной силы
// с разным поведением
EvilBadGay ebg1(loseHealthQuickly); // однотипные персонажи
EvilBadGay ebg2(loseHealthSlowly); // с разным поведением
// относительно здоровья
• Функция вычисления жизненной силы для одного и того же персонажа может изменяться во время исполнения. Например, класс GameCharacter мог бы предложить функцию-член setHealthCalculator, которая позволяет заменить текущую функцию вычисления жизненной силы.
С другой стороны, тот факт, что функция вычисления жизненной силы больше не является функцией-членом иерархии GameCharacter, означает, что она не имеет специального доступа к внутреннему состоянию объекта, чью жизненную силу вычисляет. Например, defaultHealthCalc не имеет доступа к закрытым частям EvilBadGay. Это не страшно, если жизненная сила персонажа может быть вычислена с помощью его открытого интерфейса, но для максимально точных расчетов может понадобиться доступ к закрытой информации. На самом деле такая проблема может возникать всегда, когда некоторая функциональность выносится из класса наружу (например, из функций-членов в свободные функции, не являющиеся друзьями класса, или в функции-члены другого класса, не дружественного данному). Она будет встречаться в настоящем правиле и далее, потому что все прочие проектные решения, которые нам еще предстоит рассмотреть, тоже включают использование функций, находящихся вне иерархии GameCharacter.
Общее правило таково: единственный способ рарешить функциям, не являющимся членами класса, доступ к его закрытой части – ослабить степень инкапсуляции. Например, класс может объявлять функции-нечлены в качестве друзей либо предоставлять открытые функции для доступа к тем частям реализации, которые лучше было бы оставить закрытыми. Имеет ли смысл жертвовать инкапсуляцией ради выгоды от использования указателей на функции вместо виртуальных функций (например, чтобы иметь разные функции жизненной силы для разных объектов и динамически менять их), решать вам в каждом конкретном случае.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКДанный текст является ознакомительным фрагментом.
Читайте также
Запрет кэширования посредством PHP
Запрет кэширования посредством PHP Запрет кэширования посредством PHPБольшинство сценариев формируют документы, которые при каждом запуске программы изменяются. Очевидно, если браузер пользователя начнет кэшировать такие документы, ничего хорошего не
Загрузка и скачивание файлов посредством FTP
Загрузка и скачивание файлов посредством FTP Рассмотрим, как можно загрузить свои файлы на удаленный сервер Интернета, чтобы их потом могли загружать другие, а также обсудим еще один способ загрузки файлов на свой компьютер, не связанный с использованием браузеров и
Основы указателей
Основы указателей СОМ, подобно DCE (Distributed Computing Environment – среда распределенных вычислений), ведет свое начало от языка программирования С. Хотя лишь немногие разработчики используют С для создания или использования компонентов СОМ, именно от С СОМ унаследовала синтаксис
Загрузка и выгрузка файлов посредством FTP
Загрузка и выгрузка файлов посредством FTP Поговорим о том, как можно выгрузить свои файлы на удаленный сервер Интернета, чтобы их потом могли загружать другие, а также рассмотрим еще один способ загрузки файлов на свой компьютер, не связанный с использованием браузеров и
Реализация паттерна««Шаблонный метод» с помощью идиомы невиртуального интерфейса
Реализация паттерна««Шаблонный метод» с помощью идиомы невиртуального интерфейса Начнем с интересной концепции, которая утверждает, что виртуальные функции почти всегда должны быть закрытыми. Сторонники этой школы предполагают, что правильно было бы оставить
Реализация паттерна «Стратегия» посредством класса tr::function
Реализация паттерна «Стратегия» посредством класса tr::function Если вы привыкли к шаблонам и их применению для построения неявных интерфейсов (см. правило 41), то применение указателей на функции покажется вам не слишком гибким решением. Почему вообще для вычисления
Правило 38: Моделируйте отношение «содержит» или «реализуется посредством» с помощью композиции
Правило 38: Моделируйте отношение «содержит» или «реализуется посредством» с помощью композиции Композиция – это отношение между типами, которое возникает тогда, когда объект одного типа содержит в себе объекты других типов. Например:class Address {...}; // адрес проживанияclass
Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера
Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера Контейнеры STL отличаются умом и сообразительностью. Они поддерживают итераторы для перебора как в прямом, так и в
Адаптеры указателей на функции (Adaptors for pointers to functions)
Адаптеры указателей на функции (Adaptors for pointers to functions) Чтобы позволить указателям на (унарные и бинарные) функции работать с функциональными адаптерами, библиотека обеспечивает следующее:template ‹class Arg, class Result›class pointer_to_unary_function: public unary_function‹Arg, Result› {protected: Result
Разрешение конфликтов посредством линейного зондирования
Разрешение конфликтов посредством линейного зондирования Если количество элементов, которые, скорее всего, должна содержать хеш-таблица, известно, можно выделить место для хеш-таблицы, содержащей это количество элементов и небольшое число свободных ячеек "на всякий
Разрешение конфликтов посредством связывания
Разрешение конфликтов посредством связывания Если мы готовы использовать дополнительные ячейки, кроме тех, которые требуются самой хеш-таблице, можно воспользоваться другой эффективной схемой разрешения конфликтов - схемой с закрытой адресацией. Этот метод называется
Разрешение конфликтов посредством группирования
Разрешение конфликтов посредством группирования Существует разновидность метода связывания для разрешения конфликтов, которая носит название группирования в блоки (bucketing). Вместо помещения связного списка в каждую ячейку, в нее помещается группа, которая по существу
7.9.4. Массивы указателей на функции
7.9.4. Массивы указателей на функции Можно объявить массив указателей на функции. Например:int (*testCases[10])();testCases – это массив из десяти элементов, каждый из которых является указателем на функцию, возвращающую значение типа int и не имеющую параметров.Подобные объявления
12.3.6. Реализация объекта-функции
12.3.6. Реализация объекта-функции При реализации программы в разделе 12.2 нам уже приходилось определять ряд объектов-функций. В этом разделе мы изучим необходимые шаги и возможные вариации при определении класса объекта-функции. (В главе 13 определение класса
Типы указателей
Типы указателей PBoolean Тип указателя на boolean PByte Тип указателя на byte PShortint Тип указателя на shortint PChar Тип указателя на char PSmallint Тип указателя на smallint PWord Тип указателя на word PPointer Тип указателя на pointer PInteger Тип указателя на