
Как поступить, если под рукой имеется абстрактный класс, функциональность которого хочется задействовать, но наследовать свой класс от него неохота?
Пусть имеется абстрактный класс окна
AbstractWindow с чисто виртуальным методом Create().
//------------------------------------------------------------ class AbstractWindow { public: AbstractWindow() { // код конструктора } virtual ~AbstractWindow() { // код деструктора }
virtual int Create(const char *Title)=0 { // общий код создания окна } }; //------------------------------------------------------------
|
Предположим, что некий класс
SimpleApplication, являющийся базовым классом для простых приложений, должен вызывать функцию создания окна. Таким образом, он должен включать в себя функциональность
AbstractWindow. Открытое или закрытое наследование здесь нежелательно, так как логически
SimpleApplication не связан с
AbstractWindow — приложение не является частным случаем окна.
Злоупотребляя механизмом наследования, можно создать производный от
AbstractWindow производный класс
SimpleWindow, имеющий в своем методе Create() весь необходимый арсенал для создания окна. Тогда экземпляр
SimpleWindow можно было бы включить в интерфейс
SimpleApplication и вызвать Create() в подходящий момент. Однако что, если для класса
ComplexApplication, производного от
SimpleApplication, понадобится новая реализация метода создания окна? Следует ли тогда создать производный класс
ComplexWindow, открывая таким образом разработчику единственный путь — создание новых подклассов по мере создания новых видов приложений?
Читать дальше...Возможно, такой путь оправдан, если глубина наследования от SimpleApplication невелика, а количество типов различных окон незначительно. В противном же случае удобнее воспользоваться предложенным ниже решением.
//------------------------------------------------------------ class SimpleApplication { friend class ConcreteWindow; private:
class ConcreteWindow: public AbstractWindow { public: ConcreteWindow(SimpleApplication *App): AbstractWindow(), m_App(App) {} virtual ~ConcreteWindow() {}
virtual int Create(const char *Title) { int Handle=AbstractWindow::Create(Title); m_App->OnWindowCreate(Title, Handle); return Handle; } private: SimpleApplication *m_App; };
ConcreteWindow *m_Window;
public: SimpleApplication(const char *Title) { m_Window=new ConcreteWindow(this); int Handle=m_Window->Create(Title); // остальной код конструктора } virtual ~SimpleApplication() { delete m_Window; } protected: virtual void OnWindowCreate(const char *Title, int Handle) { // код, выполняемый при создании окна } }; //------------------------------------------------------------
|
Здесь функциональная часть Create() из класса ConcreteWindow (который, кстати говоря, скрыт за ненадобностью из общей области видимости) фактически перенесена в виртуальный метод OnWindowCreate(), который может быть переопределен в подклассах. При этом гарантируется, что базовая часть кода AbstractWindow::Create() не будет забыта разработчиком и всегда будет выполнена.
Таким образом, в приведенном примере показано, как вместо двух параллельных иерархий классов получить одну.
Ниже приведен пример "из жизни" с использованием описанной выше методики избегания наследования.
В примере абстрактный класс зацикленного потока AbstractThread конкретизируется с использованием функционала Qt, посредством абстрактного же класса QThread, при этом не создается дополнительная иерархия классов и удается избежать множественного наследования.
//------------------------------------------------------------ class AbstractThread { public: AbstractThread(): m_Stop(false) {} virtual ~AbstractThread() {} virtual void Start()=0; void Stop() { m_Stop=true; Wait(); } virtual void Wait()=0; protected: void Run() { while (!m_Stop) { OnRun(); } m_Stop=false; } virtual void OnRun() {} private: bool m_Stop; }; //------------------------------------------------------------ #include <QThread> // Класс QThread абстрактный, содержит чисто виртуальный метод run(). // Ниже объявлен класс QtThread, использующий QThread // и производный только от AbstractThread class QtThread: public AbstractThread { friend class InternalQThread; private: class InternalQThread: public QThread { public: InternalQThread(QtThread *Thread): QThread(), m_Thread(Thread) {} virtual ~InternalQThread() {}
virtual void run() { m_Thread->Run(); } private: QtThread *m_Thread; };
InternalQThread *m_QThread; public: QtThread(): AbstractThread() { m_Thread=new InternalQThread(this); } virtual ~QtThread() { delete m_Thread; }
virtual void Start() { m_Thread->start(); } virtual void Wait() { m_Thread->wait(); } }; //------------------------------------------------------------
|
Цикл, выполняемый в потоке, представлен виртуальным методом OnRun(), и может быть без труда переопределен в любом производном от QtThread классе.