笔记十三:设计模式之简单工厂模式、工厂方法模式、抽象工厂模式
2016-07-14 10:43
691 查看
引言:
假设现在有一个超市(Market),超市销售饼干(Biscuit)、水果(Fruit)、饮料(Drink)三种食品。
按照常规,我们建立三个类:class Biscuit、class Fruit、class Drink。
现在假设我们有饼干2件、水果2件、饮料2件。则:
1、从上述代码中,我们发现:1)每一个具体的食品的创建都暴露在用户面前;2)假设每一类食品的数目增多,若100个,那么代码中得new 100*3次,代码冗余量变大。
2、当用户不需要了解到具体食品的创建细节,而只需要知道它是什么,同时又能减少代码冗余,有没有什么好的办法呢?
这里将简单工厂模式的结构与超市-饼干-水果-饮料对象进行映射。具体代码如下:
1、可以看出,关于饼干、饮料、水果的具体的构造操作(new)不再直接暴露在用户面前,而是封装在Market类中,第一个目的已经完成。
2、同样是每件商品均产生2个,代码中出现的new的个数仍旧只有3个,当每种食品为100个的时候,尽管实际调用了100*3次new,但写在代码中的new也还是只有3个,减少了代码冗余,完成了第2个目标。
现在,当食品种类增加,比如出现了坚果(nut)、面包(bread),那么如何修改上述代码呢?
第一:枚举类型扩充
第二:以Product作为基类的派生类增加
第三:Market类修改
这里我们发现,当增加食品种类时,Market类中switch函数需要修改,若增加的Nut和Bread操作不当,则会影响已经建立的Biscuit、Fruit、Drink。且,测试的时候,由于switch代码被修改,则其整个功能需全部重新测试,重复已完成工作量。——这就是通常意义上说的违反了开闭原则。
简单工厂模式:
优点:封装了对象的接口,减少代码冗余
缺点:耦合度较高,扩展性不好
FactoryA、FactoryB、FactoryC是新增的Factory的子类,每个子类分管自己的辖区。
1、分析代码可知,当要创建某一种食品时,只需要实例化它对应的管辖区类(例如,饼干-》饼干区超市子类BiscuitArea),结构清晰。
2、同样的,假设现在每种食品有2件,那么用户端操作的代码为:
我们发现用户端使用new的次数,并没有减少。似乎减少代码冗余这个功能没有实现???至少在用户端没有减少,我是这么认为的。
3、同样,现在新增了Nut和Bread这2种食品。如何扩展代码呢?
第一:扩展食品派生类
第二:扩展超市子类
第三:增加用户端代码
分析上述代码,可以知道,当增加Nut和Bread这2个新品种时,扩展的代码与原始代码基本上没有交集,无非是多增加一个新品种,食品扩展类增加一个派生类,超市子类增加一个,原始代码不会进行修改,因此,功能也无需重新测试。
工厂方法模式:
优点:使得具体化类的工作延迟到了子类中,耦合度低,扩展性好。工厂端符合开闭原则。
缺点:用户端代码并没有实现减少代码冗余的工作。
在工厂模式的基础上,对Product类进行修改,则可变为AdultProduct和ChildProduct类。相应地,饼干专区(BiscuitArea)的饼干也分别来自这2类中的饼干(ChildBiscuit和AdultBiscuit)。因为篇幅原因,在抽象工厂模式的UML图中,省略了水果的分类。
这里打破了产品单一的限制,可以对Product进行更为细致的划分,由一个变成了一组。
同样地,当增加Bread和Nut时,我们对之细分为成年人和儿童版。
第一:扩展儿童Nut和Bread
第二:扩展成年人Bread和Nut
第三:扩展Nut和Bread的超市子类
第四:增加用户端代码
抽象工厂模式:
优点:将“单一抽象产品类”扩展到“产品族“,产品类型增多。
缺点:结构比较臃肿,当产品增多,系统比较庞大,难以管理。
参考文献:
1、《GoF+23种设计解析附C++实现》
假设现在有一个超市(Market),超市销售饼干(Biscuit)、水果(Fruit)、饮料(Drink)三种食品。
按照常规,我们建立三个类:class Biscuit、class Fruit、class Drink。
class Biscuit{ public: Biscuit(){} ~Biscuit(){} void Show(){ cout << "Hi,customers! I'm Biscuit!" << endl; } }; class Fruit{ public: Fruit(){} ~Fruit(){} void Show(){ cout << "Hi,customers! I'm Fruit!" << endl; } }; class Drink{ public: Drink(){} ~Drink(){} void Show(){ cout << "Hi,customers! I'm Drink!" << endl; } };
现在假设我们有饼干2件、水果2件、饮料2件。则:
Biscuit* pBiscuit1 = new Biscuit(); pBiscuit1->Show(); Biscuit* pBiscuit2 = new Biscuit(); pBiscuit2->Show(); Fruit* pFruit1 = new Fruit(); pFruit1->Show(); Fruit* pFruit2 = new Fruit(); pFruit2->Show(); Drink* pDrink1 = new Drink(); pDrink1->Show(); Drink* pDrink2 = new Drink(); pDrink2->Show();
1、从上述代码中,我们发现:1)每一个具体的食品的创建都暴露在用户面前;2)假设每一类食品的数目增多,若100个,那么代码中得new 100*3次,代码冗余量变大。
2、当用户不需要了解到具体食品的创建细节,而只需要知道它是什么,同时又能减少代码冗余,有没有什么好的办法呢?
简单工厂模式
文章前面讲饼干、水果、饮料进行单独构造类,三者之间没有任何关联。为了对它们进行封装,可以根据它们之间的某种联系构造成一个整体,这样细节之处,就被封装在这个整体之中,而用户无法得知(也不需要知道)。由于三种商品属性均为食品,因此, 基类与派生类的概念被引入,这样,三种食品就有了关联。同时,“超市”这一概念有什么用呢?我们可以把食品类构造的细节操作放在超市类中,这样就完成了 封装。这里将简单工厂模式的结构与超市-饼干-水果-饮料对象进行映射。具体代码如下:
/************************************ * Function: 简单工厂模式案例 * @Copyright: * Author: by sun * Date: 2016-07-13 ************************************/ //商品名 typedef enum ProductTag { BiscuitTag, FruitTag, DrinkTag } PRODUCTTAG; //食品基类 class Product{ public: Product(){} ~Product(){} virtual void Show(){ cout << "Hi, customers! I'm Product!" << endl; } }; //饼干 class Biscuit :public Product { public: Biscuit(){} ~Biscuit(){} void Show(){ cout << "Hi,customers! I'm Biscuit!" << endl; } }; //水果 class Fruit :public Product { public: Fruit(){} ~Fruit(){} void Show(){ cout << "Hi,customers! I'm Fruit!" << endl; } }; //饮料 class Drink :public Product { public: Drink(){} ~Drink(){} void Show(){ cout << "Hi,customers! I'm Drink!" << endl; } }; //超市 class Market{ public: Market(){} ~Market(){} Product* SellProduct(PRODUCTTAG ItemTag) const; }; Product* Market::SellProduct(PRODUCTTAG ItemTag) const { switch (ItemTag) { case BiscuitTag: return new Biscuit(); break; case FruitTag: return new Fruit(); case DrinkTag: return new Drink(); break; } };
Market* pMarket = new Market(); Product* pBiscuit = pMarket->SellProduct(BiscuitTag); pBiscuit->Show(); Product* pFruit = pMarket->SellProduct(FruitTag); pFruit->Show(); Product* pDrink = pMarket->SellProduct(DrinkTag); pDrink->Show(); Product* pBiscuit1 = pMarket->SellProduct(BiscuitTag); pBiscuit1->Show(); Product* pFruit1 = pMarket->SellProduct(FruitTag); pFruit1->Show(); Product* pDrink1 = pMarket->SellProduct(DrinkTag); pDrink1->Show();
1、可以看出,关于饼干、饮料、水果的具体的构造操作(new)不再直接暴露在用户面前,而是封装在Market类中,第一个目的已经完成。
2、同样是每件商品均产生2个,代码中出现的new的个数仍旧只有3个,当每种食品为100个的时候,尽管实际调用了100*3次new,但写在代码中的new也还是只有3个,减少了代码冗余,完成了第2个目标。
现在,当食品种类增加,比如出现了坚果(nut)、面包(bread),那么如何修改上述代码呢?
第一:枚举类型扩充
//商品名 typedef enum ProductTag { BiscuitTag, FruitTag, DrinkTag, NutTag, BreadTag } PRODUCTTAG;
第二:以Product作为基类的派生类增加
//坚果 class Nut :public Product { public: Nut(){} ~Nut(){} void Show(){ cout << "Hi,customers! I'm Nut!" << endl; } }; //面包 class Bread :public Product { public: Bread(){} ~Bread(){} void Show(){ cout << "Hi,customers! I'm Bread!" << endl; } };
第三:Market类修改
switch (ItemTag) { case BiscuitTag: return new Biscuit(); break; case FruitTag: return new Fruit(); case DrinkTag: return new Drink(); break; case NutTag: return new Nut(); break; case BreadTag: return new Bread(); break; }
Market* pMarket = new Market(); Product* pBiscuit = pMarket->SellProduct(BiscuitTag); pBiscuit->Show(); Product* pFruit = pMarket->SellProduct(FruitTag); pFruit->Show(); Product* pDrink = pMarket->SellProduct(DrinkTag); pDrink->Show(); Product* pNut = pMarket->SellProduct(NutTag); pNut->Show(); Product* pBread = pMarket->SellProduct(BreadTag); pBread->Show();
这里我们发现,当增加食品种类时,Market类中switch函数需要修改,若增加的Nut和Bread操作不当,则会影响已经建立的Biscuit、Fruit、Drink。且,测试的时候,由于switch代码被修改,则其整个功能需全部重新测试,重复已完成工作量。——这就是通常意义上说的违反了开闭原则。
简单工厂模式:
优点:封装了对象的接口,减少代码冗余
缺点:耦合度较高,扩展性不好
工厂方法模式
在简单工厂模式的基础上,为了减少耦合度,提高代码的扩展性,对“工厂类”添加了一个抽象类,将工厂共同的动作抽象出来。而具体的行为由子类本身去实现,让子类决定生产什么样的产品。FactoryA、FactoryB、FactoryC是新增的Factory的子类,每个子类分管自己的辖区。
/**************************************** * Function: 工厂方法模式模式案例 * @Copyright: * Author: by sun * Date: 2016-07-13 ***************************************/ //食品基类 class Product{ public: Product(){} ~Product(){} virtual void Show(){ cout << "Hi, customers! I'm Product!" << endl; } }; //饼干 class Biscuit :public Product { public: Biscuit(){} ~Biscuit(){} void Show(){ cout << "Hi,customers! I'm Biscuit!" << endl; } }; //水果 class Fruit :public Product { public: Fruit(){} ~Fruit(){} void Show(){ cout << "Hi,customers! I'm Fruit!" << endl; } }; //饮料 class Drink :public Product { public: Drink(){} ~Drink(){} void Show(){ cout << "Hi,customers! I'm Drink!" << endl; } }; //超市 class Market{ public: Market(){}; ~Market(){}; virtual Product* SellProduct() { return new Product(); } }; //饼干区 class BiscuitArea:public Market { public: BiscuitArea(){}; ~BiscuitArea(){}; Product* SellProduct(){ return new Biscuit(); } }; //水果区 class FruitArea :public Market { public: FruitArea(){}; ~FruitArea(){}; Product* SellProduct(){ return new Fruit(); } }; //饮料区 class DrinkArea :public Market { public: DrinkArea(){}; ~DrinkArea(){}; Product* SellProduct(){ return new Drink(); } };
Market* pBiscuitArea = new BiscuitArea(); Product* pBiscuit = pBiscuitArea->SellProduct(); pBiscuit->Show(); Market* pFruitArea = new FruitArea(); Product* pFruit = pFruitArea->SellProduct(); pFruit->Show(); Market* pDrinkArea = new DrinkArea(); Product* pDrink = pDrinkArea->SellProduct(); pDrink->Show();
1、分析代码可知,当要创建某一种食品时,只需要实例化它对应的管辖区类(例如,饼干-》饼干区超市子类BiscuitArea),结构清晰。
2、同样的,假设现在每种食品有2件,那么用户端操作的代码为:
Market* pBiscuitArea = new BiscuitArea(); Product* pBiscuit = pBiscuitArea->SellProduct(); pBiscuit->Show(); Market* pFruitArea = new FruitArea(); Product* pFruit = pFruitArea->SellProduct(); pFruit->Show(); Market* pDrinkArea = new DrinkArea(); Product* pDrink = pDrinkArea->SellProduct(); pDrink->Show();
Market* pBiscuitArea1 = new BiscuitArea();
Product* pBiscuit1 = pBiscuitArea1->SellProduct();
pBiscuit1->Show();
Market* pFruitArea1 = new FruitArea();
Product* pFruit1 = pFruitArea1->SellProduct();
pFruit1->Show();
Market* pDrinkArea1 = new DrinkArea();
Product* pDrink1 = pDrinkArea1->SellProduct();
pDrink->Show();
我们发现用户端使用new的次数,并没有减少。似乎减少代码冗余这个功能没有实现???至少在用户端没有减少,我是这么认为的。
3、同样,现在新增了Nut和Bread这2种食品。如何扩展代码呢?
第一:扩展食品派生类
//坚果 class Nut :public Product { public: Nut(){} ~Nut(){} void Show(){ cout << "Hi,customers! I'm Nut!" << endl; } }; //面包 class Bread :public Product { public: Bread(){} ~Bread(){} void Show(){ cout << "Hi,customers! I'm Bread!" << endl; } };
第二:扩展超市子类
//坚果区 class NutArea :public Market { public: NutArea(){}; ~NutArea(){}; Product* SellProduct(){ return new Nut(); } }; //面包区 class BreadArea :public Market { public: BreadArea(){}; ~BreadArea(){}; Product* SellProduct(){ return new Bread(); } };
第三:增加用户端代码
Market* pBiscuitArea = new BiscuitArea(); Product* pBiscuit = pBiscuitArea->SellProduct(); pBiscuit->Show(); Market* pFruitArea = new FruitArea(); Product* pFruit = pFruitArea->SellProduct(); pFruit->Show(); Market* pDrinkArea = new DrinkArea(); Product* pDrink = pDrinkArea->SellProduct(); pDrink->Show();
Market* pNutArea = new NutArea();
Product* pNut = pNutArea->SellProduct();
pNut->Show();
Market* pBreadArea = new BreadArea();
Product* pBread = pBreadArea->SellProduct();
pBread->Show();
分析上述代码,可以知道,当增加Nut和Bread这2个新品种时,扩展的代码与原始代码基本上没有交集,无非是多增加一个新品种,食品扩展类增加一个派生类,超市子类增加一个,原始代码不会进行修改,因此,功能也无需重新测试。
工厂方法模式:
优点:使得具体化类的工作延迟到了子类中,耦合度低,扩展性好。工厂端符合开闭原则。
缺点:用户端代码并没有实现减少代码冗余的工作。
抽象工厂模式
当前饼干、水果、饮料都统称为食品。超市决定为了使顾客吃的更健康,决定对食品进行分类:成年人食品+儿童食品。则分类如下:在工厂模式的基础上,对Product类进行修改,则可变为AdultProduct和ChildProduct类。相应地,饼干专区(BiscuitArea)的饼干也分别来自这2类中的饼干(ChildBiscuit和AdultBiscuit)。因为篇幅原因,在抽象工厂模式的UML图中,省略了水果的分类。
/*************************************** * Function: 抽象工厂模式 * @Copyright: * Author: by sun * Date: 2016-07-13 **************************************/ class ChildProduct{ public: ChildProduct(){} ~ChildProduct(){} virtual void Show(){ cout << "Hi,customers! I'm ChildProduct" << endl; } }; class ChildBiscuit:public ChildProduct { public: ChildBiscuit(){} ~ChildBiscuit(){} void Show(){ cout << "Hi,customers! I'm ChildBiscuit" << endl; } }; class ChildDrink :public ChildProduct { public: ChildDrink(){} ~ChildDrink(){} void Show(){ cout << "Hi,customers! I'm ChildDrink" << endl; } }; class AdultProduct { public: AdultProduct(){} ~AdultProduct(){} virtual void Show(){ cout << "Hi, customers! I'm AdultProduct" << endl; } }; class AdultBiscuit:public AdultProduct { public: AdultBiscuit(){} ~AdultBiscuit(){} void Show(){ cout << "Hi,customers! I'm AdultBiscuit" << endl; } }; class AdultDrink :public AdultProduct { public: AdultDrink(){} ~AdultDrink(){} void Show(){ cout << "Hi,customers! I'm AdultDrink" << endl; } }; class Market { public: Market(){} ~Market(){} virtual AdultProduct* SellAdultProduct(){ return new AdultProduct(); } virtual ChildProduct* SellChildProduct(){ return new ChildProduct(); } }; class BiscuitArea :public Market { public: BiscuitArea(){} ~BiscuitArea(){} AdultProduct* SellAdultProduct(){ return new AdultBiscuit(); } ChildProduct* SellChildProduct(){ return new ChildBiscuit(); } }; class DrinkArea :public Market { public: DrinkArea(){} ~DrinkArea(){} AdultProduct* SellAdultProduct(){ return new AdultDrink(); } ChildProduct* SellChildProduct(){ return new ChildDrink(); } };
Market* pBiscuitArea = new BiscuitArea(); ChildProduct* pChildBiscuit = pBiscuitArea->SellChildProduct(); pChildBiscuit->Show(); AdultProduct* pAdultBiscuit = pBiscuitArea->SellAdultProduct(); pAdultBiscuit->Show(); Market* pDrinkArea = new DrinkArea(); ChildProduct* pChildDrink = pDrinkArea->SellChildProduct(); pChildDrink->Show(); AdultProduct* pAdultDrink = pDrinkArea->SellAdultProduct(); pAdultDrink->Show();
这里打破了产品单一的限制,可以对Product进行更为细致的划分,由一个变成了一组。
同样地,当增加Bread和Nut时,我们对之细分为成年人和儿童版。
第一:扩展儿童Nut和Bread
class ChildNut :public ChildProduct { public: ChildNut(){} ~ChildNut(){} void Show(){ cout << "Hi,customers! I'm ChildNut" << endl; } }; class ChildBread :public ChildProduct { public: ChildBread(){} ~ChildBread(){} void Show(){ cout << "Hi,customers! I'm ChildBread" << endl; } };
第二:扩展成年人Bread和Nut
class AdultNut :public AdultProduct { public: AdultNut(){} ~AdultNut(){} void Show(){ cout << "Hi,customers! I'm AdultNut" << endl; } }; class AdultBread :public AdultProduct { public: AdultBread(){} ~AdultBread(){} void Show(){ cout << "Hi,customers! I'm AdultBread" << endl; } };
第三:扩展Nut和Bread的超市子类
class NutArea :public Market { public: NutArea(){} ~NutArea(){} AdultProduct* SellAdultProduct(){ return new AdultNut(); } ChildProduct* SellChildProduct(){ return new ChildNut(); } }; class BreadArea :public Market { public: BreadArea(){} ~BreadArea(){} AdultProduct* SellAdultProduct(){ return new AdultBread(); } ChildProduct* SellChildProduct(){ return new ChildBread(); } };
第四:增加用户端代码
Market* pBiscuitArea = new BiscuitArea(); ChildProduct* pChildBiscuit = pBiscuitArea->SellChildProduct(); pChildBiscuit->Show(); AdultProduct* pAdultBiscuit = pBiscuitArea->SellAdultProduct(); pAdultBiscuit->Show(); Market* pDrinkArea = new DrinkArea(); ChildProduct* pChildDrink = pDrinkArea->SellChildProduct(); pChildDrink->Show(); AdultProduct* pAdultDrink = pDrinkArea->SellAdultProduct(); pAdultDrink->Show();
Market* pNutArea = new NutArea();
ChildProduct* pChildNut = pNutArea->SellChildProduct();
pChildNut->Show();
AdultProduct* pAdultNut = pNutArea->SellAdultProduct();
pAdultNut->Show();
Market* pBreadArea = new BreadArea();
ChildProduct* pChildBread = pBreadArea->SellChildProduct();
pChildBread->Show();
AdultProduct* pAdultBread = pBreadArea->SellAdultProduct();
pAdultBread->Show();
抽象工厂模式:
优点:将“单一抽象产品类”扩展到“产品族“,产品类型增多。
缺点:结构比较臃肿,当产品增多,系统比较庞大,难以管理。
参考文献:
1、《GoF+23种设计解析附C++实现》
相关文章推荐
- 南海问题
- 常见机器学习算法比较
- UI Automation Test
- java核心技术第一卷
- css画三角形
- AbstractQueuedSynchronizer(七)——Share模式doAcquireShared
- Android最新版本开发环境搭建图文教程
- Java-----Apache Commons IO
- java获取文件属性的方法
- 安全智能TF卡读写稳定性测试
- springmvc实现浏览器下载文件
- C语言的一些特殊用法(#define)
- 【JZOJ 4606】 序列
- URLConnection 和HttpURLConnection
- linux(centos)搭建SVN服务器
- ACM 求素数 线性筛法 o[n]
- 易语言取外网ip的两种方法
- intent.setFlags方法中的参数值内涵
- Markdown2
- 【多线程】BlockingQueue详解