Нет, стандартного шаблона абсолютно не существует.
Мой совет - избегать ловушек при проектировании ОО-новичков в попытке найти единый общий интерфейс "базового класса" для всего.Ваш Дракон сильно отличается от вашего Мастера, и вы ничего не выиграете, пытаясь объединить два интерфейса, кроме симпатичных абстракций кода.Помните, наследование не является инструментом для повторного использования кода.
Самый простой подход для каждого объекта - поддерживать свое собственное внутреннее состояние (как вы уже описали) и иметь возможность рисовать это состояние.Именно здесь инструкция переключения старой школы может быть более понятной, чем использование полиморфизма для того же эффекта.Пример:
class Dragon {
enum State { asleep, flying, breathing_fire };
public:
void draw () { /* old-school switch */
switch (cur_state){
case asleep: draw_asleep ();
case flying: draw_flying ();
case breathing_fire: draw_breathing_fire();
}
}
private:
void draw_asleep ();
void draw_flying ();
void draw_breathing_fire();
};
Опытные разработчики C ++ будут задыхаться от приведенного выше кода (как они должны), потому что оператор switch - это почти то же, что и вызов полиморфного метода - диспетчеризация во время выполнения.(Или даже более загадочно: таблица адресов методов.) Мое личное мнение таково, что для этого конкретного типа класса, то есть для одного открытого интерфейса, инкапсулирующего конечный автомат, оператор switch более понятен и удобен в обслуживании, поскольку делает конечный автоматпрыгать явно.Я думаю, также ясно, что я понимаю, что это обычно плохой дизайн C ++.
Трения этой конструкции вызваны разделительной линией между конечным автоматом и единственным открытым интерфейсом.Спящий дракон явно не то же самое, что летящий дракон.На самом деле, он не будет иметь ничего общего.Но дизайн более высокого уровня говорит о том, что они ЖЕ дракон.Какой беспорядок!Вы создаете различные объекты Дракона для каждого государства?Нет, потому что это раскрыло бы концепцию состояния для вызывающей стороны.Вы создаете различные внутренние вспомогательные объекты для каждого состояния и отправляете в них?Возможно, но это быстро запутывается.Вышеуказанный подход является компромиссом между ними.Думайте о выражении switch как об изоляции безобразия.Открытый интерфейс чист, как и частный интерфейс.Только оператор switch содержит грязный беспорядок.Рассмотрите этот подход, а также рассмотрите другие предложения.
Наконец, чтобы нарисовать вашего Волшебника и вашего Дракона, достаточно простого шаблона-обертки или функтора:
struct Draw {
template <class S>
void operator()(S& s) { s.draw (); }
};
Дело в том, чтовам не нужно объединять два разных класса только потому, что они поддерживают одну и ту же логическую операцию.