这篇文章将要讲的是个人对于书上两个模式讲解的理解和思考。
大话中给了一个例子,做一个收款的例子,有三个输入:一件物品的单价,这件物品购买的数量,折扣选项。(这里只对一件物品进行计价)
操作起来就是获得一件物品的数量count和单价perprice,然后计算出总的费用total,同时根据折扣计算最终价格finaltotal。
折扣有三种:(1)正常不打折(什么也不做)(2)打八折(total×0.8)(3)满300减一百(finaltotal=total - int(total/300)*100)
如何实现呢?
可以先来思考一下,有几步(拿控制台命令来讲)
1 输入物品单价,数量和折扣选项
2 先计算出total价格,再根据不同的折扣选项进行分支运算得到不同的价钱返回
一、工厂模式实现方法
先不说工厂模式是什么,我们看看它是怎么做的。
abstract class CashSuper { public abstract double acceptCash(double money); //现金计算虚基类 } class Cash_normal: CashSuper { public override double acceptCash(double money) { return money; //啥都不干,原价返回 } } class Cash_manjian: CashSuper { public override double acceptCash(double money) { return(money-int(money/300)*100) //满300减100 } } class Cash_dazhe: CashSuper { public override double acceptCash(double money) { return money* 0.8; //打八折 } } class Factory { public CashSuper CashSuperCreator(string Type) // 根据不同的选项,生产不同的类 { switch(Type) { case normal: Cash_normal cn=new Cash_normal(); cs=cn; break; case manjian: Cash_manjian cm=new Cash_manjian(300,100); cs=cm; break; case dazhe: Cash_dazhe cd=new Cash_dazhe(0.8); cs=cd; break; default: cout<<"input not supported"; break; } return cs; } } public void main(){ //输入参数 cout<<"please input the consume type"; string selectedType, int count; double perprice,total,finaltotal; cin>>selectedType>>count>>perprice; //计算总价 total=count*perprice; //通过不同的选择,由工厂生产不同的类 CashSuper cs=CashFactory cf(selectedType); finaltotal=cs.acceptCash(total); } summary
我们可以看到,其实这个问题的最关键点就在于怎么根据折扣选项的不同做选项。
工厂模式的方法是,分别提供现金计算的虚基类和实现类,以及工厂产生类。 现金计算只负责现金怎么算,工厂只负责怎么产。
形象点说现金计算只负责提供原材料,而工厂类则负责怎么产,也就是怎么进行类的对象实例化。
工厂类其实都算不上各类,只是包含了一个普通成员函数,原材料输入参数和输出参数的种类都是人家别人的,它自己只负责实例化一下其他啥都不管的。
再罗嗦点来说,就是代工工厂,苹果人家设计了好东西,别人提供了原料,它只负责代工组装,输入的是人家的思想和原料,输出的还是人家苹果的成品,自己在产品上除了连made in China其他什么都留不下。
它只具有一层类的外壳,用数据结构来说,类包含了数据和操作方法,而工厂类则实质上只具有操作方法而已。
通过工厂模式,我们发现那些根据选项进行switch分支实例化这些逻辑语句都放到后台实现了。这就是工厂模式的好处。
但是这工厂模式有坏处,看这句话
CashSuper cs=CashFactory cf(selectedType);
我们看了后台知道这是Factory工厂类调用CashSuper方法生成了CashSuper子类的对象,又有Factory又有CashSuper的,能不能将这两个东西化减成一个东西出现在前台呢?
这是下下个部分三 要讲的。
反正我们至少知道通过工厂类,那些分支实例化的东西都可以用工厂类封装起来。
二、策略模式的实现。
abstract class CashSuper { public abstract double acceptCash(double money); } class Cash_normal: CashSuper { public override double acceptCash(double money) { return money; } } class Cash_manjian: CashSuper { Cash_manjian(double man, double jian) { } public override double acceptCash(double money) { return(money-int(money/300)*100) } } class Cash_dazhe: CashSuper { public override double acceptCash(double money) { return money* 0.8; } } class Context { //the inherits will automatically be converted to base class CashSuper cs; public Context(CashSuper cs_choice) //constructor { this.cs=cs_choice; } public CashSuper getResult(double total) { return cs.acceptCash(total); } } public void main(){ cout<<"please input the consume type"; string selectedType, int count; double perprice,total,finaltotal; cin>>selectedType>>count>>perprice; total=count*perprice; switch(selectedType) { case "normal": Context context(new Cash_normal()) break; case "manjian": Context context(new Cash_manjian()) break; case "dazhe": Context context(new Cash_dazhe()) //我们可以看到整个客户端程序都是Context类,CashSuper 都没有出现。 break; //CashSuper的类的成员和方法已经被策略类Context给完完全全封装进去了 default: cout<<"input not supported"; break; } c_strategy.getResult(total); } summary策略类的实现如上,
和工厂类只负责大生产实例化不一样,策略类已经把类的对象和操作方法包进去了。
在程序中我们可以看到整个客户端都是Context的身影,CashSuper类的内容几乎全进去了。
这也是我们为什么会说策略类用来封装类的,严严实实的我们从外边根本看不见。
策略类有自己的数据成员也即 CashSuper类的对象cs,因为有CashSuper对象和之前的虚方法,我们也可以编些函数操作这些对象,因此策略类能够使用类的对象和方法。
程序执行switch语句中,我们可以根据不同的折扣option,选择生成特定对象传到我们的Context类对象里面,由getResult方法可以使用CashSuper类的方法。
但是
有一点我们之前也说到:策略类已经把类的对象和操作方法包进去了,可以你看看程序,其实还是能够发现CashSuper的蛛丝马迹,在哪里呢?
Context context(new Cash_normal())你可以发现,策略类尽管把CashSuper的数据和方法都包含进去了,理论上应该没什么问题了,但是我们一旦实例化对象的时候,new Cash_normal这些CashSuper的小爪牙还是会出现的。
这个问题源于,策略类将类的数据和方法都封装了,但是实例化这个操作没有封装进来。。等等
实例化?
不是工厂模式擅长的么?于是我又要废话了。。。
三、策略模式和工厂模式结合
abstract class CashSuper { public abstract double acceptCash(double money); } class Cash_normal: CashSuper { public override double acceptCash(double money) { return money; } } class Cash_manjian: CashSuper { Cash_manjian(double man, double jian) { } public override double acceptCash(double money) { return(money-int(money/300)*100) } } class Cash_dazhe: CashSuper { public override double acceptCash(double money) { return money* 0.8; } } class Context { //the inherits will automatically be converted to base class CashSuper cs; public Context(string Type) //Context不传对象进来了,而是根据折扣选择类型 { //通过工厂模式来生成对应的对象。 switch(Type) { case normal: Cash_normal cn=new Cash_normal(); cs=cn; break; case manjian: Cash_manjian cm=new Cash_manjian(300,100); cs=cm; break; case dazhe: Cash_dazhe cd=new Cash_dazhe (0.8); cs=cd; break; default: cout<<"input not supported"; break; } } public CashSuper getResult(double total) { return cs.acceptCash(total); } } public void main(){ cout<<"please input the consume type"; string selectedType, int count; double perprice,total,finaltotal; cin>>selectedType>>count>>perprice; total=count*perprice; Context context(selectedType); context.getResult(total); } summary
在Context类里面,我们发现了,
Context不传对象进来了,而是根据折扣选择类型,而是工厂模式生成相应的对象。
于是我们在main程序中现在只看到了Context一个类,也看不到根据不同的折扣生成不同的对象这些乱七八糟的东西了。
这些都被我们用策略模式和工厂模式搞到内部了。
总结:
其实我们发现工厂模式和策略模式,其实根本不是像中文英文这样是完全分开的,他们叫模式,其实说实话就是针对某些特殊问题的对应方法。
工厂模式就是管生产的,精确说就是管CashSuper cs=new CashSuper()这类实例化的。这是对动态操作的封装。
策略模式就是管封装类的,可以把好多个类都搞进来。这是对静态东西的封装(类的对象和方法其实都是静态的,方法在那里,我不调用它还是静态的)
再从目的上来讲,
为什么要用工厂模式啊,因为这个类初始化这个,那个类实例化那个对象,各种实例化操作全交给工厂负责。
为什么要用策略模式啊,这个例子就需要用的,我们需要根据不同选择调用不同的现金计算类完成计算,那么用策略类和多态,我们就可以完成三个类调用的工作。所以类包含多个类并不是吃饱了没事干。
但是无论工厂模式还是策略模式,封装是目的,继承是基础,而多态则是机理保证。
三个normal,dazhe,manjian的子类其实都是通过cashsuper这个基类作为入口传进函数的,然后再在函数内部通过多态实现的。
PS。我都想不到,这么个简单的例子会有这么多话可以讲,不学不知道,不学则无术啊。如果你看到这里,希望对你有帮助