MFC之工厂模式(C++ & java)

MFC之工厂模式(C++ & java)


之前对面向对象了解不多,设计模式更不用提了,现在只能遇到一个学一个了,今天看了工厂模式,两个版本的,C++和java的,思想都是一样的,我都列出来了。文章都是转过来的,非原创哦

C++版:

简单工厂模式
生活例子

吃饭是人的基本需求,如果人类不需要吃饭,可能我们就能活得清闲许多,也就不需要像现在一样没日没夜的工作,学习。我们学习是为了找到更好的工作,好工作为了赚更多的钱,最终为了吃饱饭,吃好饭。因此可以说吃饭是与人息息相关,下面就从吃饭的例子来引入工厂模式的学习。

如果你想吃饭了,怎么办自己做吗?自己做就相当于程序中直接使用new。当然是自己下个指令,别人来做更爽。那就把做饭的任务交给你的老婆吧,那么她就是一个做饭的工厂了,你告诉她要要吃红烧肉,等会她就从厨房给你端出来一盘香喷喷的红烧肉了,再来个清蒸鱼吧,大鱼大肉不能太多,那就再来个爆炒空心菜,最后再来个西红柿鸡蛋汤。下图 1) 就是这个问题的模型。
 
(图1)
显然到了这里,你是Client,你老婆就是工厂,她拥有做红烧肉的方法,做清蒸鱼的方法,做爆炒空心菜、西红柿鸡蛋汤的方法,这些方法返回值就是食物抽象。红烧肉、清蒸鱼、爆炒空心菜、西红柿鸡蛋汤就是食物的继承类,到这里你就可以大吃二喝了。简单工厂模式也成型了。哈哈,娶一个手艺不错的老婆还真好,吃的好,吃的爽,又清闲。

下面来看标准的简单工厂模式的分析。

意图

把一系列拥有共同特征的产品的创建封装

结构图


(图2)

角色分析
产品基类: 工厂创建的所有产品的基类, 它负责描述所有实例所共有的公共接口。它用来作为工厂方法的返回参数。
代码实现:
//---这时一个系列的产品基类
  1. class Product  
  2. {  
  3. protected:  
  4.     Product(void);  
  5. public:  
  6.     virtual ~Product(void);  
  7. public:  
  8.     virtual void Function() = 0;  
  9. };//cpp  
  10.   
  11. Product::Product(void){}  
  12. Product::~Product(void){}  


具体产品类:产品1和产品2,这个角色实现了抽象产品角色所定义的接口。代码实现:
  1. //产品A  
  2. class ConcreteProductA:public Product  
  3. {  
  4. public:  
  5.     ConcreteProductA(void);  
  6. public:  
  7.     virtual ~ConcreteProductA(void);  
  8. public:  
  9.     virtual void Function();  
  10. };//cpp  
  11.   
  12. ConcreteProductA::ConcreteProductA()  
  13. {  
  14.     cout<<"创建 A 产品"<<endl;  
  15. }  
  16. ConcreteProductA::~ConcreteProductA()  
  17. {  
  18.     cout<<"释放 A 产品"<<endl;  
  19. }  
  20. void ConcreteProductA::Function()  
  21. {  
  22.     cout<<"这是产品 A 具有的基本功能"<<endl;  
  23. }//产品B与A类似不这里不再给出,大家可以下载源码  

工厂类:负责具体产品的创建,有两种方式实现产品的创建,I、创建不同的产品用不同的方法;II、创建不同产品用相同的方法,然后通过传递参数实现不同产品的创建。本实例中两种模式都给出了,大家自行分析。
  1. //简单工厂,此类不需要继承,直接硬编码实现生成的产品  
  2. class SimpleFactory  
  3. {  
  4. public:  
  5.     SimpleFactory(){}  
  6. public:  
  7.     ~SimpleFactory(){}  
  8. public:  
  9.     Product *CreateProduct(int ProuctType);  
  10.     Product *CreateProductA();  
  11.     Product *CreateProductB();  
  12. };//CPP  
  13.   
  14. Product * SimpleFactory::CreateProduct(int ProductType=0)  
  15. {  
  16.     Product *p = 0;  
  17.     switch(ProductType)  
  18.     {  
  19.     case 0:  
  20.         p= new ConcreteProductA();  
  21.         break;  
  22.     case 1:  
  23.         p= new ConcreteProductB();  
  24.         break;  
  25.     default:  
  26.         p= new ConcreteProductA();  
  27.         break;  
  28.     }  
  29.     return p;  
  30. }  
  31.   
  32. Product *SimpleFactory::CreateProductA()  
  33. {  
  34.     return new ConcreteProductA();  
  35. }  
  36.   
  37. Product *SimpleFactory::CreateProductB()  
  38. {  
  39.     return new ConcreteProductB();  
  40. }  

客户端程序 :访问的角色包括产品基类、工厂类。不直接访问具体产品类。通过基类指针的多态实现产品功能的调用。
访问描述 :客户程序通过调用工厂的方法返回抽象产品,然后执行产品的方法。
  1. //调用代码  
  2. SimpleFactory sf;  
  3. Product *p = sf.CreateProductA();  
  4. p->Function();  
  5. delete p;  
  6. p = sf.CreateProductB();  
  7. p->Function();  
  8. delete p;  
优缺点说明

优点:1) 首先解决了代码中大量New的问题。为何要解决这个问题,好处的说明我想放到结尾总结中。
2) 用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。 
缺点:对修改不封闭,新增加产品您要修改工厂。违法了鼎鼎大名的开闭法则(OCP)。

附加说明
  • 大家可以参看 吕震宇 老师的C#设计模式(四)参看这个模式的分析,里面还给出了这个模式的两个变体,实现比较简单,有兴趣的朋友可以自行用C++实现一下。
  • 产品基类的代码中构造函数我用了Protected,而没有使用Public,主要是为了体现编码中的一个最小权限原则。说明此类不许用户直接实例化。虽然这里使用了virtual void Function() = 0;编译器也会控制不让用户直接实例化,不过我依然认为使用私有化构造函数来保护类不直接实例化是一个良好的编程风格。
工厂方法模式
生活例子:

人是最贪得无厌的动物,老婆手艺再好,总有不会做的菜,你想吃回锅肉,怎么办,让老婆学呗,于是就给她就新增了做回锅肉的方法,以后你再想吃一个新菜,就要给你老婆新加一个方法,显然用老婆做菜的缺点也就暴露出来了,用程序设计的描述就是对修改永远不能封闭。当然优点也是有的,你有了老婆这个工厂,这些菜不用你自己做了,只要直接调用老婆这个工厂的方法就可以了。

面对上面对修改不能封闭的问题,有没有好的解决方案吗,如果你有钱,问题就迎刃而解了,把老婆抽象变成一个基类,你多娶几个具体的老婆,分别有做鱼的,做青菜的,炖汤的老婆,如果你想吃一个新菜,就再新找个女人,从你的老婆基类继承一下,让她来做这个新菜。显然多多的老婆这是所有男人的梦想,没有办法,法律不允许,那么咱们只是为了做饭,老婆这个抽象类咱们不叫老婆了,叫做厨师吧,她的子类也自然而然的该叫做鱼的厨师、炖汤的厨师了。现在来看这个模式发生了变化,结构中多了一个厨师的抽象,抽象并不具体的加工产品了,至于是炖汤还是炖鱼,是由这个抽象工厂的继承子类来实现,现在的模式也就变成工厂方法模式了,这个上面的结构图1)就变成了下面的图3的结构了。
 
(图3)

现在再来分析现在的模式,显然简单工厂的缺陷解决了,新增加一个菜只需要新增加一个厨师就行了,原来的厨师还在做原来的工作,这样你的设计就对修改封闭了。你看把老婆解放出来,招聘大量的厨师到你家里这个方案多么的完美,你老婆也会爱死你了。当然前提就是你要有多多的钱噢,当然这里的钱的多少在软件领域应该看你的客户软件投资方的要求。
下面来一下标准的工厂模式的实现

意图
  • 定义一个用户创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
  • 上面是GOF关于此模式的意图描述,我想补充的是您可以这样理解:为了改善简单工厂对修改不能关闭的问题。
结构



图4

角色分析
产品基类:同简单工厂的产品基类,其实就是用和简单工厂中的是同一个类,这里并没有重写。
具体产品类:也是用的简单工厂的具体产品类,为了体现对修改的关闭这里为系统新添加了一个具体产品类,就是“新产品”,代码中叫做“ConcreteProductANew”
工厂基类:定义了工厂创建产品的接口,但是没有实现,具体创建工作由其继承类实现。
代码实例 
  1. <span style="font-size:10px;">//工厂模式,此模式的工厂只定义加工产品的接口,具体生成交予其继承类实现  
  2. //只有具体的继承类才确定要加工何种产品  
  3. class Factory  
  4. {  
  5. public:  
  6.     Factory(void);  
  7. public:  
  8.     virtual ~Factory(void);  
  9. public:  
  10.     virtual Product* CreateProduct(int ProductType = 0) =0;  
  11. };//CPP  
  12.   
  13. Factory::Factory(void){}  
  14. Factory::~Factory(void){}</span>  

具体工厂类 :工厂基类的具体实现,由此类决定创建具体产品,这里  ConcreteFactory1  对于与图中的  工厂实现 ConcreteFactory2  对于与图中的 新工厂
下面给出实现代码
  1. <span style="font-size:10px;">//工厂实现  
  2. class ConcreteFactory1:public Factory  
  3. {  
  4. public:  
  5.     ConcreteFactory1();  
  6. public:  
  7.     virtual ~ConcreteFactory1();  
  8. public :  
  9.     Product* CreateProduct(int ProductType);  
  10. };//新工厂,当要创建新类是实现此新工厂  
  11. class ConcreteFactory2:public Factory  
  12. {  
  13. public:  
  14.     ConcreteFactory2();  
  15. public:  
  16.     virtual ~ConcreteFactory2();  
  17. public :  
  18.     Product* CreateProduct(int ProductType);  
  19. };//CPP  
  20.   
  21. ConcreteFactory1::ConcreteFactory1(){}  
  22. ConcreteFactory1::~ConcreteFactory1(){}  
  23.   
  24. Product * ConcreteFactory1::CreateProduct(int ProductType = 0)  
  25. {  
  26.     Product *p = 0;  
  27.     switch(ProductType)  
  28.     {  
  29.     case 0:  
  30.         p= new ConcreteProductA();  
  31.         break;  
  32.     case 1:  
  33.         p= new ConcreteProductB();  
  34.         break;  
  35.     default:p= new ConcreteProductA();  
  36.         break;  
  37.     }  
  38.     return p;  
  39. }  
  40.   
  41. ConcreteFactory2::ConcreteFactory2(){}  
  42. ConcreteFactory2::~ConcreteFactory2(){}  
  43. Product * ConcreteFactory2::CreateProduct(int ProductType = 0)  
  44. {  
  45.     return new ConcreteProductANew();  
  46. }</span>  

客户端调用 :访问角色(产品基类、工厂基类、工厂实现类)
调用描述:客户程序通过工厂基类的方法调用工厂实现类用来创建所需要的具体产品。从而实现产品功能的访问。
代码实现
  1. <span style="font-size:10px;">Factory*fct = new ConcreteFactory1();  
  2. Product *p = fct->CreateProduct(0);  
  3. p->Function();  
  4. delete p;  
  5. p = fct->CreateProduct(1);  
  6. p->Function();  
  7. delete p;  
  8. delete fct;  
  9. fct = new ConcreteFactory2();  
  10. p=fct->CreateProduct();  
  11. delete p;  
  12. delete fct;</span>  

优缺点分析

优点

  • 简单工厂具有的优点
  • 解决了简单工厂的修改不能关闭的问题。系统新增产品,新增一个产品工厂即可,对抽象工厂不受影响。

缺点:对于创建不同系列的产品无能为力

适用性
  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
其他参考
抽象工厂模式
生活例子

世事多变,随着时间的推移,走过的地方越来越多,你天南海北的朋友也越来越多。你发现菜原来还分了许多菜系,鲁菜、粤菜、湘菜等等,它们各有各的风味,同样是红烧肉由不同菜系出来的味道也各不相同, 你招待不同的朋友要用不同的菜系,这下难办了,你的厨师都是鲁菜风味,怎么办,广东的朋友来了吃不惯。现在我们再回到简单工厂模式(就是老婆做菜的模式),我们把红烧肉再向下继承,生成鲁菜红烧肉、粤菜红烧肉、湘菜红烧肉;清蒸鱼向下继承为鲁菜清蒸鱼、粤菜清蒸鱼、湘菜清蒸鱼,其它也以此类推。我们也修改一下老婆的这个类,不让其返回食物基类,而是返回红烧肉、清蒸鱼、爆炒空心菜、西红柿鸡蛋汤这一层次,并把这些方法抽象化,作为菜系工厂基类,然后再从此基类继承出,鲁菜工厂、粤菜工厂、湘菜工厂等等,再由这些具体工厂实现创建具体菜的工作,哈哈你如果招待广东朋友就用粤菜工厂,返回的就是一桌粤菜菜系的红烧肉、清蒸鱼、空心菜和西红柿鸡蛋汤了,你的广东朋友一定会吃的非常合乎胃口了。噢,非常好,你已经实现了抽象工厂模式了。结构模型图也变成了下图 6)的样子了。

(图6)
现在可以看到,想新来做一个菜系,只需新聘请一个厨师就可以了,多么完美,但是你先别高兴太早,如果你想新增加一个菜就变得非常困难了。

意图

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 
结构

角色分析
产品基类:这里包含产品基类A和产品基类B,实际上在我的示例代码中,这两个产品都从共同的基类继承而来,但是这个继承关系却是在这个模式之外的部分,而本身这个模式关心的是这两个产品基类的差异部分。
代码实现:这里的代码就是借用的简单工厂模式中具体产品类的代码实现部分,为了大家阅读方便,下面重新给出一下。
  1. <span style="font-size:10px;">//产品A  
  2. class ConcreteProductA:public Product  
  3. {  
  4. public:  
  5.     ConcreteProductA(void);  
  6. public:  
  7.     virtual ~ConcreteProductA(void);  
  8. public:  
  9.     virtual void Function();  
  10. };//cpp  
  11.   
  12. ConcreteProductA::ConcreteProductA()  
  13. {  
  14.     cout<<"创建 A 产品"<<endl;  
  15. }  
  16. ConcreteProductA::~ConcreteProductA()  
  17. {  
  18.     cout<<"释放 A 产品"<<endl;  
  19. }  
  20. void ConcreteProductA::Function()  
  21. {  
  22.     cout<<"这是产品 A 具有的基本功能"<<endl;  
  23. }//产品B与A类似不这里不再给出</span>  

具体产品类:这里的具体产品类是产品A1,A2,B1、B2等,
代码实现:A1对应的实现就是“”
  1. class ConcreteProductA1:public ConcreteProductA  
  2. {  
  3. public:  
  4.     ConcreteProductA1(void);  
  5. public:  
  6.     virtual ~ConcreteProductA1(void);  
  7. public:  
  8.     virtual void Function();  
  9. };//CPP  
  10.   
  11. ConcreteProductA1::ConcreteProductA1()  
  12. {  
  13.     cout<<"创建 A1 产品"<<endl;  
  14. }  
  15. ConcreteProductA1::~ConcreteProductA1()  
  16. {  
  17.     cout<<"释放 A1 产品"<<endl;  
  18. }  
  19. void ConcreteProductA1::Function()  
  20. {  
  21.     cout<<"这时产品 A1 具有的基本功能"<<endl;  
  22. }  

工厂抽象接口:定义了创建产品的接口,这里返回参数是返回的产品A,产品B,而本身产品A和B的共同基类,小弟认为正是这个特征构成了抽象工厂和工厂模式的区别。
代码实现
  1. <span style="font-size:10px;">class AbstractFactory  
  2. {  
  3. public:  
  4.     AbstractFactory();  
  5. public:  
  6.     virtual ~AbstractFactory();  
  7. public:  
  8.     virtual ConcreteProductA* CreateA() = 0;  
  9.     virtual ConcreteProductB* CreateB() = 0;  
  10. };//CPP  
  11. AbstractFactory::AbstractFactory(){}  
  12. AbstractFactory::~AbstractFactory(){}</span>  

具体工厂实现类:工厂1和工厂2。新增加系列,只需新实现一个工厂。
代码实现: 工厂1的就是ConcreteAbsFactory1,工厂2的代码类似,这里没有给出,可以在下载代码中看到
  1. class ConcreteAbsFactory1:public AbstractFactory  
  2. {  
  3. public:  
  4.     ConcreteAbsFactory1();  
  5. public:  
  6.     virtual ~ConcreteAbsFactory1();  
  7. public:  
  8.     virtual ConcreteProductA* CreateA();  
  9.     virtual ConcreteProductB* CreateB();  
  10. };//CPP  
  11. ConcreteAbsFactory1::ConcreteAbsFactory1(){}  
  12. ConcreteAbsFactory1::~ConcreteAbsFactory1(){}  
  13.   
  14. ConcreteProductA* ConcreteAbsFactory1::CreateA()  
  15. {  
  16.     return new ConcreteProductA1();  
  17. }  
  18.   
  19. ConcreteProductB * ConcreteAbsFactory1::CreateB()  
  20. {  
  21.     return new ConcreteProductB1();  
  22. }  

客户端访问: 访问角色(产品基类、抽象工厂、具体工厂实现类)
访问描述: 通过抽象工厂的指针访问具体工厂实现来创建对应系列的产品,然后通过产品基类指针访问产品功能。
调用代码:
  1. AbstractFactory *absfct = new ConcreteAbsFactory1();  
  2. ConcreteProductA *cpa = absfct->CreateA();  
  3. cpa->Function();delete cpa;  
  4. ConcreteProductB *cpb = absfct->CreateB();  
  5. cpb->Function();  
  6. delete cpb;  
  7. delete absfct;  
  8. absfct = new ConcreteAbsFactory2();  
  9. cpa = absfct->CreateA();cpa->Function();  
  10. delete cpa;  
  11. cpb = absfct->CreateB();  
  12. cpb->Function();  
  13. delete cpb;  


和工厂模式的分析比较

现在可以和工厂模式对比一下,抽象工厂返回的接口不再是产品A和产品B的共同基类Product了,而是产品A、产品B基类(在工厂模式中它们为具体实现类,这里变成了基类)了。此时工厂的抽象和简单工厂中的工厂方法也很类似,就是这些特征区使其别于工厂模式而变成抽象工厂模式了,因此抽象工厂解决的是创建一系列有共同风格的产品(鲁菜还是粤菜),而工厂方法模式解决的创建有共同特征的一系列产品(红烧肉、清蒸鱼它们都是食物)。当然简单工厂的缺陷在抽象工厂中又再次出现了,我要新增加一个产品,工厂抽象接口就要改变了。因此抽象工厂并不比工厂模式完美,只不过是各自的适用领域不同而已。其实,这里如果把抽象工厂模式的接口返回产品A和产品B的共同基类(工厂模式返回的参数),你会发现,奇怪这个模式怎么这么眼熟,它不是恰恰退化成工厂模式了。
类模式与对象模式的区别讨论:先看定义类“模式使用继承关系,把对象的创建延迟的子类,对象模式把对象的创建延迟到另一个对象中”。 分析:首先它们创建对象都不是在基类中完成,都是在子类中实现,因此都符合类模式的概念;但是工厂模式的创建产品对象是在编译期决定的,要调用某个工厂固定的,而抽象工厂模式对产品的创建是在运行时动态决定的,只有到运行时才确定要调用那个工厂,调用工厂随运行环境而改变。(这里我一直很混乱,欢迎大家讨论)

适用性
  • 一个系统要独立于它的产品的创建、组合和表示时
  • 一个系统要由多个 产品系列中的一个来配置时
  • 当你要强调一个系列相关的产品对象的设计以便进行联合使用时
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。 

java版:

一、引子
       话说十年前,有一个暴发户,他家有三辆汽车——Benz奔驰、Bmw宝马、Audi奥迪,还雇了司机为他开车。不过,暴发户坐车时总是怪怪的:上Benz车后跟司机说“开奔驰车!”,坐上Bmw后他说“开宝马车!”,坐上Audi说“开奥迪车!”。你一定说:这人有病!直接说开车不就行了?!
       而当把这个暴发户的行为放到我们程序设计中来时,会发现这是一个普遍存在的现象。幸运的是,这种有病的现象在OO(面向对象)语言中可以避免了。下面就以Java语言为基础来引入我们本文的主题:工厂模式。

二、分类
      工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

工厂模式在《Java与模式》中分为三类:
       1)简单工厂模式(Simple Factory)

2)工厂方法模式(Factory Method)

3)抽象工厂模式(Abstract Factory)
       这三种模式从上到下逐步抽象,并且更具一般性。
       GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。

两者皆可,在本文使用《Java与模式》的分类方法。下面来看看这些工厂模式是怎么来“治病”的。

 

三、简单工厂模式

简单工厂模式又称静态工厂方法模式。重命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。
       先来看看它的组成:

1)        工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。

2)        抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。

3)        具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

来用类图来清晰的表示下的它们之间的关系(如果对类图不太了解,请参考我关于类图的文章): 

那么简单工厂模式怎么来使用呢?我们就以简单工厂模式来改造暴发户坐车的方式——现在暴发户只需要坐在车里对司机说句:“开车”就可以了。

//抽象产品角色

public interface Car{

public void drive(); 
}

 

//具体产品角色
public class Benz implements Car{

public void drive()  {

System.out.println("Driving Benz ");

}

}

public class Bmw implements Car{

public void drive()  {

System.out.println("Driving Bmw ");

}

}
。。。(奥迪我就不写了:P)

//工厂类角色

public class Driver{

//工厂方法.注意 返回类型为抽象产品角色
       public static Car driverCar(String s)throws Exception    {

              //判断逻辑,返回具体的产品角色给Client
              if(s.equalsIgnoreCase("Benz"))  

                     return new Benz();

              else if(s.equalsIgnoreCase("Bmw"))

                     return new Bmw();

                    ......   
             else throw new Exception();

       。。。


//欢迎暴发户出场......

public class Magnate{

       public static void main(String[] args){

              try{ 
                     //告诉司机我今天坐奔驰              
                     Car car = Driver.driverCar("benz"); 
                     //下命令:开车                   
                     car.drive();

              。。。

    将本程序空缺的其他信息填充完整后即可运行。如果你将所有的类放在一个文件中,请不要忘记只能有一个类被声明为public。本程序在jdk1.4 下运行通过。

      程序中各个类的关系表达如下:

 

这便是简单工厂模式了。怎么样,使用起来很简单吧?那么它带来了什么好处呢?
       首先,使用了简单工厂模式后,我们的程序不在“有病”,更加符合现实中的情况;而且客户端免除了直接创建产品对象的责任,而仅仅负责“消费”产品(正如暴发户所为)。

       下面我们从开闭原则(对扩展开放;对修改封闭)上来分析下简单工厂模式。当暴发户增加了一辆车的时候,只要符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一辆车,都要在工厂类中增加相应的业务逻辑或者判断逻辑,这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。对于这样的工厂类(在我们的例子中是为司机师傅),我们称它为全能类或者上帝类。
       我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,也累坏了我们这些程序员:(
       于是工厂方法模式作为救世主出现了。 

四、工厂方法模式

       工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。

你应该大致猜出了工厂方法模式的结构,来看下它的组成:

1)        抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

2)        具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

3)        抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

4)        具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

用类图来清晰的表示下的它们之间的关系:

工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活起来——当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。可以看出工厂角色的结构也是符合开闭原则的!

       我们还是老规矩,使用一个完整的例子来看看工厂模式各个角色之间是如何来协调的。话说暴发户生意越做越大,自己的爱车也越来越多。这可苦了那位司机师傅了,什么车它都要记得,维护,都要经过他来使用!于是暴发户同情他说:看你跟我这么多年的份上,以后你不用这么辛苦了,我给你分配几个人手,你只管管好他们就行了!于是,工厂方法模式的管理出现了。代码如下:

//抽象产品角色,具体产品角色与简单工厂模式类似,只是变得复杂了些,这里略。
//抽象工厂角色
public interface Driver{
       public Car driverCar();
}
public class BenzDriver implements Driver{
       public Car driverCar(){
              return new Benz();
       }
}
public class BmwDriver implements Driver{
       public Car driverCar()   {

return new Bmw(); 
       }
}

//应该和具体产品形成对应关系...
//有请暴发户先生
 public class Magnate

{

              public static void main(String[] args)

              {

                     try{ 
                            Driver driver = new BenzDriver();

                            Car car = driver.driverCar();

                            car.drive();

                     }

       ……

}

可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。

 

五、小结

工厂方法模式仿佛已经很完美的对对象的创建进行了包装,使得客户程序中仅仅处理抽象产品角色提供的接口。那我们是否一定要在代码中遍布工厂呢?大可不必。也许在下面情况下你可以考虑使用工厂方法模式:

1)        当客户程序不需要知道要使用对象的创建过程。

2)        客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。

 

简单工厂模式与工厂方法模式真正的避免了代码的改动了?没有。在简单工厂模式中,新产品的加入要修改工厂角色中的判断语句;而在工厂方法模式中,要么将判断逻辑留在抽象工厂角色中,要么在客户程序中将具体工厂角色写死(就象上面的例子一样)。而且产品对象创建条件的改变必然会引起工厂角色的修改。

       面对这种情况,Java的反射机制与配置文件的巧妙结合突破了限制——这在Spring中完美的体现了出来。

 

六、抽象工厂模式

      先来认识下什么是产品族: 位于不同产品等级结构中,功能相关联的产品组成的家族。还是让我们用一个例子来形象地说明一下吧。

 

图中的BmwCar和BenzCar就是两个产品树(产品层次结构);而如图所示的BenzSportsCar和BmwSportsCar就是一个产品族。他们都可以放到跑车家族中,因此功能有所关联。同理BmwBussinessCar和BenzSportsCar也是一个产品族。
      回到抽象工厂模式的话题上。

可以说,抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。

抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象

而且使用抽象工厂模式还要满足一下条件:

1)        系统中有多个产品族,而系统一次只可能消费其中一族产品。

2)        同属于同一个产品族的产品以其使用。

来看看抽象工厂模式的各个角色(和工厂方法的如出一辙):

1)        抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

2)        具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。

3)        抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

4)        具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

类图如下:

看过了前两个模式,对这个模式各个角色之间的协调情况应该心里有个数了,我就不举具体的例子了。只是一定要注意满足使用抽象工厂模式的条件哦。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值