Шаг 108 - Универсальные классы

Мы постоянно, что-то программируем. Например, доступ к элементам массива. При этому очень часто решения, которые мы с Вами реализуем являются частными. То есть для частного случая. Надо что то исправить начинаем писать снова. Плохо это. С++ позволяет нам делать универсальные решения. Вот как это реализуется мы с Вами и посмотрим. Идея вот в чем. мы хотим в программе использовать массив. Что мы делаем? Берем класс допустим CStringArray и начинаем. Но самое интересное что если вдруг не дай бог придется изменить базовый класс то надо исправлять кучу кода. Выход из данной ситуации в использовании механизмов виртуальных функций и наследовании. Давайте посмотрим схему.

108_1.gif (2337 b)

У нас есть основной самый первый класс CBaseAbstract, в нем просто описаны функции, которые нам необходимы без их реализации. Вот такая пустышка.

class CBaseAbstract  
{
public:
	CBaseAbstract();
	virtual ~CBaseAbstract();
	virtual void Add(CString s);
	virtual void Remove(int iIndex);
	virtual CString GetAt(int iIndex);
	virtual int GetCount();
};

CBaseAbstract::CBaseAbstract()
{}

CBaseAbstract::~CBaseAbstract()
{}

void CBaseAbstract::Add(CString s)
{}

void CBaseAbstract::Remove(int iIndex)
{}

CString CBaseAbstract::GetAt(int iIndex)
{return "";}

int CBaseAbstract::GetCount()
{return 0;}

И есть класс, который умеет вызывать функции этой пустышки или класса наследника.


class CWork  
{
public:
	CWork(CBaseAbstract *pointer);
	~CWork();
	void Add(CString s);
	void Remove(int iIndex);
	CString GetAt(int iIndex);
	int GetCount();
private:
	CBaseAbstract* cPointer;
};

CWork::CWork(CBaseAbstract *pointer)
{
cPointer=pointer;
}

CWork::~CWork()
{}

void CWork::Add(CString s)
{
	cPointer->Add(s); 
}

void CWork::Remove(int iIndex)
{
	cPointer->Remove(iIndex); 
}

CString CWork::GetAt(int iIndex)
{
	return cPointer->GetAt(iIndex); 
}

int CWork::GetCount()
{
	return cPointer->GetCount(); 
}

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

#include "stdafx.h"
#include "MFCArray.h"
#include "StlArray.h"
#include "work.h"
#include "iostream.h"


void main()
{
	CMFCArray mfcArray;
	CStlArray stlArray;
	CWork cw1(&mfcArray);
	CWork cw2(&stlArray);
	cw1.Add("Hello"); cw2.Add("Hello");
	cw1.Add("Abstract"); cw2.Add("Abstract");
	cout << cw1.GetCount() << " " << cw2.GetCount()  << endl;
	cout  << cw1.GetAt(1) << " " << cw2.GetAt(1) << endl;
}

Естественно, что классы CStlArray и CMFCArray я породил от CBaseAbstract, но внутренняя реализация каждого класса своя.

#include "BaseAbstract.h"
#include "afxcoll.h"

class CMFCArray : public CBaseAbstract 
{
public:
	CString GetAt(int iIndex);
	int GetCount();
	void Add(CString s);
	CMFCArray();
	virtual ~CMFCArray();
private:
	CStringArray ca;
};

#include "BaseAbstract.h"
#include "iostream.h"
#include "vector"
#include "afxwin.h"


using namespace std ;
typedef vector<CString> STLStringArray;


class CStlArray  : public CBaseAbstract
{
public:
	CString GetAt(int iIndex);
	int GetCount();
	void Add(CString s);
	CStlArray();
	virtual ~CStlArray();
private:
	STLStringArray ca;
};

Реализация методов совершенно тривиальная и Вы можете посмотреть ее в проекте. Использование данной технологии дает вам широкие возможности к повторному использованию кода. Например для баз данных создали свои классы CBaseAbstract и CBaseWork и работайте хоть с DAO хоть с ADO или еще с чем. С какой то черты, а точнее с класса в данном случае CBaseWork код Ваш меняться не будет при заменен метода доступа к данным. А значит и элементы управления универсальные и так далее. Вся работа будет сосредоточена на производном классе от CBaseAbstract.


Загрузить проект | Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 16.07.2001