Dongle烧写模块重构(四)--用工厂模式将方案商从功能代码中抽离

如果我们采取了策略模式,那么哪些东西是由我们(客户)掌握的,哪些是有方案商来决定的呢?

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)的这段代码,而现在,就达到了这样的目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值