如果我们采取了策略模式,那么哪些东西是由我们(客户)掌握的,哪些是有方案商来决定的呢?
1.行为簇的基类可以由我们来确定,然后让方案商更具我们的行为簇基类取实现其具体的算法。
2.我们编写代码的时候,只需要这样就行了,调用某方案商的某个行为:
方案商X-》行为Y
然后框架就会根据方案商针对行为的具体实现调用到具体的算法了。
比如我们现在有要实现一个BurnDongle(烧写Dongle)的功能模块,收到上层的命令之后,需要用到方案商的scanDongle、setDongleMode和burnDongle这三个行为接口,当前,我们在创建方案商对象的时候会用这样的直接创建的方式:
void DongleModule::BurnDongle(int providerId)
{
//create a vendor
VendorNanosic *nanosic = new VendorNanosic();
//step 1 scandongle
nanosic->scanDongle();
//step 2 setDongleMode
nanosic->setDongleMode(DONGLE_MODE_NORMAL);
//step 3 burnDongle
nanosic->burnDongle();
}
直接new了一个具体行为的实例化类对象,然后就可以调用到具体行为类的方法了。但我们要更换方案商的时候,就要打开DongleModule::BurnDongle()这段代码来一个个更换,是很不方便的,我们第一想到的自然就是 将这些实例化的代码抽离出来(还是”分离出变化的部分,保留不变的部分“这一指导思想)
整个过程如下:
1.DongleModule::BurnDongle函数里的对象抽取到main函数中:
//create a VendorFactory
VendorFactory *factory = new VendorFactory();
//create DongleModule
DongleModule dongleModule(factory);
//invoke the function by vendortype
dongleModule.BurnDongle(VENDOR_IFLYTEK);
2.工厂的设计:
class VendorFactory
{
public:
/** Default constructor */
VendorFactory();
/** Default destructor */
virtual ~VendorFactory();
//工厂中有一个对象句柄
BaseVendor *vendor;
//有一个方法用来创建对象
BaseVendor* CreateVendor(int vendorId);
};
3.工厂创建对象函数的实现:
//工厂根据传入的id参数来创建相应的对象
BaseVendor* VendorFactory::CreateVendor(int vendorId)
{
LOGE("VendorFactory::CreateVendor,vendorId = %d",vendorId);
if(vendorId == VENDOR_IFLYTEK)
{
vendor= new VendorIflytek();
}else if(vendorId == VENDOR_NANOSIC)
{
vendor= new VendorNanosic();
}
return vendor;
}
在工厂的这个函数里面,根据传入的参数来选择要创建的对象。
这里要看到vendor是BaseVendor类型的一个指针,而VendorIflytek,VendorNanosic是这个类簇里的派生类。
通过这个函数就初始化了工厂里的方案商对象。这个方案商对象是在DongleModel里被使用的
4.DongleModule的设计:
class DongleModule
{
public:
/** Default constructor */
DongleModule();
//reference of factory object
VendorFactory *vendorFactory;
BaseVendor *vendor;
//init factory object in creator
DongleModule(VendorFactory *factory);
/** Default destructor */
virtual ~DongleModule();
void BurnDongle(VendorType vendorId);
};
DongleModule就是我们最大的一个类,也就是我们的Dongle模块了,这个类里有一些函数,比如BurnDongle.在这个函数里面,使用了某个方案商的一系列接口来实现我们需要实现的烧写Dongle这个功能。因此我们以BurnDongle这个函数看下我们为了实现烧写Dongle这个功能,是怎么做的:
5.在工厂中创建DongleModule的方案商对象,然后调用方案商相应函数:
void DongleModule::BurnDongle(VendorType vendorId)
{
//get vendor from factory
vendor = vendorFactory->CreateVendor(int vendorId)
//step 1 scandongle
vendor->scanDongle();
//step 2 setDongleMode
vendor->setDongleMode(DONGLE_MODE_NORMAL);
//step 3 burnDongle
vendor->burnDongle();
}
先前有说,这些scanDongle,getDongleInfo,setDongleToIspMode都是方案商需要实现的行为的基类,也就是行为簇的接口,针对这些接口,方案商有多种不同的具体实现。对我们来说,我们只需要像上面那样
a.利用工厂创建某个方案商
//get vendor from factory
vendor = vendorFactory->CreateVendor(int vendorId)
b.利用方案商封装好的函数
//step 1 scandongle
vendor->scanDongle();
//step 2 setDongleMode
vendor->setDongleMode(DONGLE_MODE_NORMAL);
//step 3 burnDongle
vendor->burnDongle();
来实现我们的功能就OK了(BurnDongle)。
6.测试代码运行结果如下:
7.我们现在分析一下,才用工厂,相比较与不采用工厂,会有哪些好处:
如果不采用工厂,我们Dongle模块需要实现BurnDongle,InitDongle这些模块的时候,都是在BurnDongle,InitDongle这些函数里
先创建方案商:
BaseVendor *vendor = new VendorIflytek();
然后调用方法:
//step 1 scandongle
vendor->scanDongle();
//step 2 setDongleMode
vendor->setDongleMode(DONGLE_MODE_NORMAL);
//step 3 burnDongle
vendor->burnDongle();
这种对方案商对象的创建每个函数里面都有
然而采用工厂之后:
只像测试函数里那样将方案商id传入:
//invoke the function by vendortype
dongleModule.BurnDongle(VENDOR_IFLYTEK);
我们的代码里:
void DongleModule::BurnDongle(VendorType vendorId)
{
LOGE("DongleModule::BurnDongle");
//get vendor from factory
vendor = vendorFactory->CreateVendor(vendorId);
//step 1 scandongle
vendor->scanDongle();
//step 2 setDongleMode
vendor->setDongleMode(DONGLE_MODE_NORMAL);
//step 3 burnDongle
vendor->burnDongle();
}
方安商变了,你是从A 换成了B还是C也好,实现BurnDongle的代码不需要修改
实现BurnDongle这个功能的时候需要调用到的scanDongle采用的具体算法变了,你是采用RC,蓝牙还是NEC也好,实现BurnDongle的代码也不需要修改
这就是采用工厂的好处,将方案商这些可能会变的集中在工厂里处理。
其实这也是符合一个人对一个功能的正常认知的。我要实现BurnDongle这个功能,我需要先ScanDongle,然后在GetDongleInfo,然后再SetDongleToIspMode,这是我烧写Dongle的逻辑,和采用哪家方案商,方案商采用何种实现是没有关系的。
如果设计良好的话,当且仅当我烧写Dongle的逻辑发生了变化,我才需要来修改我DongleModule::BurnDongle(VendorType vendorId)的这段代码,而现在,就达到了这样的目的。