笔记十三:设计模式之简单工厂模式、工厂方法模式、抽象工厂模式

时间:2022-02-28 06:56:04

引言:
假设现在有一个超市(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++实现》