1.对象型别依存性
例如:
类结构
class Animal{};
class Cat : public Animal{};
class Dog : public Animal{};
实例猫的对象
Animal* pb = new Cat();
这里出现了具体型别Cat。如果我们要构造狗的实例,就需要编码时将这句话中的Cat改成Dog。也就是编译期必须完全确知对象的类型。
虚特性的特点是执行期才确定对象类型,那么显然new操作符并不具备虚函数的特点。
能否在执行期时识别对象型别,生成对象呢?
2.执行期识别--虚构造函数
按照上述构想,执行期时获得对象类型Cat,传给Animal对象的统一的创建方法,创建正确的对象并把指向基类的指针返回。这分明就是把构造方法改造成虚函数。可惜C++中没有虚构造函数。new操作符也不支持虚特性。
变通的方法是在工厂中提供CreateAnimal的方法,把Animal的类别传进去,用switch/case识别,然后构造正确的实例
Animal* AnimalFactory::CreateAnimal( int animalType )
{
switch ( animalType )
{
case Cat:
return new Cat();
case Dog:
return new Dog();
}
return null;
}
这样实现了执行期动态识别对象型别。
问题是它执行了switch,有了switch语句的最大缺点,不利于程序扩展。当增加一个类Bird时,需要到AnimalFactory.cpp中增加一种对象类型,还要到switch中增加一个case,还要到增加一个#include "bird.h"。增加一个Bird类,却要在AnimalFactory类中作这么多改变,增加了类之间的耦合性,显然这不是一个好的设计。
3.可伸缩工厂
当你增加一种产品时,不必修改工厂的代码,这就是可伸缩工厂,这就是我们想得到的。
实现方法是在将创建产品方法中的switch识别改为函数指针识别,同时维护一张对象型别到函数指针的映射表。如:
Cat->Animal* CreateCat()
Dog->Animal* CreateDog()
Bird->Animal* Bird()
类型Cat和CreateCat方法在cat.cpp中提供,并把Cat到CreateCat的映射注册到Factory中的映射表中。
依次类推,如果增加Bird类,只要在Bird.cpp中增加Bird的类型ID和CreateBird方法,并调用Factory的Regist方法,把这个映射注册到Factory中即可,不涉及一点Factory的代码的修改。
可以用std:map作这个映射表。
修改后的Factory如下
class AnimalFactory
{
public:
typedef Animal* (*CreateAnimalCallback)();
typedef std::map< int, CreateAnimalCallback > AnimalMap;
Animal* CreateAnimal( int animalType );
bool RegistAnimal( int animalType, CreateAnimalCallback callback );
private:
AnimalMap map_;
}
Animal* AnimalFactory::CreateAnimal( int animalType )
{
AnimalMap::const_iterator i = map_.find( animalType );
if ( i == map_.end() )
{
throw std::runtime_error("Unknown Shape ID");
}
return ( i->sencond)();
}
bool AnimalFactory::RegistAnimal( int animalType, CreateAnimalCallback callback )
{
return map_.insert( AnimalMap::value_type( animalType, callback ) ).second;
}
在cat.h中
const int Cat = 1; //定义全局变量,猫的型别标示
Animal* CreateAnimal(); //定义全局函数,用于生成猫的对象。
class Cat : public Animal{}; //定义猫的类。
4.Symbian中实现可伸缩的对象工厂
Symbian中不能用std::map这个数据类型,所以需要自己实现map表。
我写了一个类TMapModel,里面有两个成员变量,一个TUid,一个指向函数的指针,从而完成一对映射。
在Factory类中,用RArray<TMapModel>,将所有映射关系组合起来,作为Map表。
具体实现如下
(1) mapmodel.h //映射关系头文件
#ifndef T_MAPMODEL_H
#define T_MAPMODEL_H
#include <e32base.h>
class CAknView;
class TMapModel
{
public:
typedef CAknView* (*CreateViewCallback)();
TMapModel( TUid aViewId, CreateViewCallback aCreateCallback );
inline TUid ViewId()
{
return iViewId;
};
inline CreateViewCallback CreateCallback()
{
return iCreateCallback;
};
private:
TUid iViewId;
CreateViewCallback iCreateCallback;
};
#endif
(2) mapmodel.cpp
#include "mapmodel.h"
TMapModel::TMapModel( TUid aViewId, CreateViewCallback aCreateCallback )
:iViewId( aViewId ), iCreateCallback( aCreateCallback )
{
}
(3) viewfactory.h // 工厂类头文件
#ifndef C_VIEWFACTORY_H
#define C_VIEWFACTORY_H
#include <e32base.h>
#include "mapmodel.h"
class CViewFactory : public CBase
{
public:
static CViewFactory* NewL();
static CViewFactory* NewLC();
~CViewFactory();
TInt RegistViewL( TUid aViewId, TMapModel::CreateViewCallback aCreateCallback );
CAknView* CreateViewL( TUid aViewId );
private:
void ConstructL();
private:
RArray<TMapModel> iMap;
};
#endif //C_VIEWFACTORY_H
(4) viewfactory.cpp //工厂类的实现
#include <aknview.h>
#include "mapmodel.h"
#include "viewfactory.h"
CViewFactory* CViewFactory::NewL()
{
CViewFactory* self = CViewFactory::NewLC();
CleanupStack::Pop();
return self;
}
CViewFactory* CViewFactory::NewLC()
{
CViewFactory* self = new (ELeave) CViewFactory();
CleanupStack::PushL( self );
self->ConstructL();
return self;
}
CViewFactory::~CViewFactory()
{
iMap.Close();
}
void CViewFactory::ConstructL()
{
}
TBool CViewFactory::RegistViewL( TUid aViewId, TMapModel::CreateViewCallback aCreateCallback )
{
iMap.AppendL( TMapModel( aViewId, aCreateCallback ) );
return ETrue;
}
CAknView* CViewFactory::CreateViewL( TUid aViewId )
{
CAknView* view = NULL;
TInt i = 0;
for ( i = 0; i < iMap.Count(); i++ )
{
if ( iMap[i].ViewId() == aViewId )
{
TMapModel::CreateViewCallback callback =
iMap[i].CreateCallback();
return callback();
}
}
if ( i == iMap.Count() )
{
User::Leave( KErrNotFound );
}
return view;
}
(5) 使用方法
在appui类的构造方法中实例化factory类,然后进行类的注册。
如
void CMyAppUi::ConstructL()
{
BaseConstructL( EAknEnableSkin );
iViewFactory = CViewFactory::NewL();
// Regist view
iViewFactory->RegistViewL( KCatViewId, CreateCatViewL );
iViewFactory->RegistViewL( KDogViewId, CreateDogViewL );
// Create view
CAknView* view = iViewFactory->CreateViewL( KCatId );
CleanupStack::PushL( view );
AddViewL( view );
SetDefaultViewL( *view );
CleanupStack::Pop();
}