Куб 0
Как поступить, если под рукой имеется абстрактный класс, функциональность которого хочется задействовать, но наследовать свой класс от него неохота?
Пусть имеется абстрактный класс окна 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 классе.

@темы: Трюки с наследованием