一、工厂模式是什么?
是C++多态的一种很好的具体表现。通过继承,重写抽象父类的虚函数,并在main函数中通过基类指针指向子类对象的一种编码风格
工厂模式分为三种(简单工厂模式,工厂方法模式,抽象工厂模式)
二、为什么要使用工厂模式?
参考原文链接:https://blog.csdn.net/flyingcloud6/article/details/131352604
解释1、
1.1 如果每次需要的对象构造过程都比较复杂,那么就需要很多行的代码去能去创建一个对象。这样就比较麻烦!
1.2 如果在别的类中又需要创建该对象,那么代码的重复度就很高。
1.3 需要通过一个工具类,把每个对象创建的具体逻辑给隐藏起来,且只对外分别暴露一个调用方法。这样就减少了代码量,以后如果想改代码的话,只需要改一处即可,也方便我们日常的维护。
1.4 该工具类就是工厂模式思想落地实现的一种具体情况,现在只不过很多框架都会自带工厂模式的实现接口。一般不需要我们自己手写,只需简单配置一下就可以使用了。
解释2、
主要是对对象的创建进行了一个封装;
因此也属于创建型模式。
目的:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
优点
一个调用者想创建一个对象,只要知道其名称就可以了;
扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以;
屏蔽产品的具体实现,调用者只关心产品的接口。
缺点
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
三、简单工厂模式(Simple Factory)
结构组成:
工厂类(SimpleFactory):工厂模式的核心类,会定义一个用于创建指定的具体实例对象的接口。
抽象产品类(Car):是具体产品类的继承的父类或实现的接口。
具体产品类(BWM\Audi):工厂类所创建的对象就是此具体产品实例。
优点与不足:
优点: 结构简单,管理方式简单
缺点: 扩展性非常差,新增产品的时候,需要去修改工厂类。
举例子:具体产品类BMW、Audi,抽象产品类Car,工厂类SimpleFactory;
抽象产品类:用于存放一类特征相似的实现,并用于定义具体产品类。
工厂类:用于统计所有的特征,可以根据enum
类型创建具体的产品对象。
class Car
{
public:
Car(string name):_name(name){}
virtual void show()=0;
protected:
std::string _name;
};
class BMW:public Car
{
public:
BMW(string name):Car(name){}
void show() {
cout << "这是一辆宝马" << endl;
}
};
class Audi :public Car
{
public:
Audi(string name) :Car(name){}
void show() {
cout << "这是一辆奥迪" << endl;
}
};
好处:
可以看到,简单工厂可以做到,让用户创建对象的时候只需要知道对象的名称(BMW、AUDI)就好,而不需要关心创建对象的细节(BMW是如何建造的、型号是什么等等细节)。
不足:
每当我们想要扩展对象的时候(增加BENZ的对象)就需要在SimpleFactory类中添加代码,增加switch后面的case选项。这样一来,就需要修改源代码。灵活性非常的差!!!
那么,能不能做到添加对象的时候,不对现有代码进行修改呢?(也就是我们开发软件时候需要遵守的开-闭原则)
四、工厂模式
一个具体的工厂 生产一个具体的产品。
结构组成:
抽象工厂类(Factory):工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。
具体工厂类(BMWFactory \AudiFactory ):继承于抽象工厂,实现创建对应具体产品对象的方式。
抽象产品类(Car):它是具体产品继承的父类(基类)。
具体产品类(BWM\Audi):具体工厂所创建的对象,就是此类。
优点与不足:
优点: 工厂方法模式抽象出了工厂类,并把具体产品对象的创建放到具体工厂类实现。实现了一个工厂生产一类产品,不需要修改工厂类,只需要增加新的具体工厂类即可。
缺点: 每新增一个产品,就需要增加一个对应的产品的具体工厂类。相比简单工厂模式而言,工厂方法模式需要更多的类定义。
抽象工厂类,提供了创建具体工厂类的纯虚函数,并通过具体工厂类来返回具体产品类;
抽象产品类同上;
举例子:
抽象工厂 + 具体工厂
//工厂方法:
class Factory
{
public:
virtual Car* createCar(string name) = 0;
};
//宝马工厂
class BMWFactory : public Factory
{
public:
Car* createCar(string name)
{
return new BMW(name);
}
};
//奥迪工厂
class AudiFactory : public Factory
{
public:
Car* createCar(string name)
{
return new Audi(name);
}
};
void main()
{
Factory* bmwfty = new BMWFactory();
Factory* audifty = new AudiFactory();
Car* p1 = bmwfty->createCar("X6");
Car* p2 = audifty->createCar("A6");
p1->show();
p2->show();
// 释放资源
delete adidasShoes;
delete adidasProducer;
}
抽象产品 + 具体产品:
class Car
{
public:
Car(string name):_name(name){}
virtual void show()=0;
protected:
std::string _name;
};
class BMW:public Car
{
public:
BMW(string name):Car(name){}
void show() {
cout << "这是一辆宝马" << endl;
}
};
class Audi :public Car
{
public:
Audi(string name) :Car(name){}
void show() {
cout << "这是一辆奥迪" << endl;
}
};
当我们想要增加新的工厂:比如说奔驰工厂的时候,根本不用去管宝马和奥迪,只需要增加一个新的工厂就行了,所以说是符合了软件设计的 “开闭原则” : 对已有的功能关闭,对扩展开放
好处:
可以做到不同的产品,在不同的工厂里面创建(模块化非常清晰),能够对现有工厂,以及产品的修改关闭
不足:
实际上,很多产品是有关联关系的,属于一个产品簇,不应该放到不同的工厂里面去创建,这样
一是不符合实际的产品对象创建逻辑,二是工厂类太多了,不好维护
五、抽象工厂模式
一个具体的工厂可生产多个产品。
工厂模式 已经能够满足基本的要求了,但是在实际生活中,比如宝马企业,不止有汽车,还有别的系列产品,比如:车灯、轮胎。。。也就是跟汽车有关的一组产品。这就需要“抽象工厂模式”。
重要区别:
工厂模式:只能生产一个产品。(要么BWM车、要么Audi车)
抽象工厂:可以一下生产一个产品族(里面有很多产品:BWM车\BWM车灯、Audi车\Audi车灯)
结构组成(和工厂模式一样):
抽象工厂类厂(AbstractFactory):工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。
具体工厂类(BMWFactory\AudiFactory):继承于抽象工厂,实现创建对应具体产品对象的方式。
抽象产品类(Car \Light):它是具体产品继承的父类(基类)。
具体产品类(BMWCar\AudiCar、BmwLight\AudiLight):具体工厂所创建的对象,就是此类。
优点与不足:
优点: 提供一个接口,可以创建多个产品族中的产品对象,同一类的多个产品对象不需要创建多个工厂。
缺点: 相比简单工厂模式而言,抽象工厂模式需要更多的类定义。
举例子:
抽象产品类 + 具体产品类
//系列产品1:
class Car
{
public:
Car(string name):_name(name){}
virtual void show()=0;
protected:
std::string _name;
};
class BMW:public Car
{
public:
BMW(string name):Car(name){}
void show() {
cout << "这是一辆宝马" << endl;
}
};
class Audi :public Car
{
public:
Audi(string name) :Car(name){}
void show() {
cout << "这是一辆奥迪" << endl;
}
};
//系列产品 2:
class Light
{
public:
virtual void show()=0;
};
class BmwLight :public Light
{
public:
void show()
{
cout << "BMW 的 Light" << endl;
}
};
class AudiLight :public Light
{
public:
void show()
{
cout << "Audi 的 Light" << endl;
}
};
抽象工厂类 + 具体工厂类:
//工厂方法 ==>> 抽象工厂(对一组关联关系的产品簇提供产品对象的统一创建)
class Factory
{
public:
virtual Car* createCar(string name) = 0;//工厂方法 创建汽车
virtual Light* createLight() = 0;// 工厂方法 创建汽车关联的产品,车灯
};
//宝马工厂
class BMWFactory : public Factory
{
public:
Car* createCar(string name)
{
return new BMW(name);
}
Light* createLight()
{
return new BmwLight();
}
};
//奥迪工厂
class AudiFactory : public Factory
{
public:
Car* createCar(string name)
{
return new Audi(name);
}
Light* createLight()
{
return new AudiLight();
}
};
void main()
{
Factory* bmwfty = new BMWFactory();
Factory* audifty = new AudiFactory();
Car* p1 = bmwfty->createCar("X6");
Car* p2 = audifty->createCar("A6");
p1->show();
p2->show();
Light* l1 = bmwfty->createLight();
Light* l2 = audifty->createLight();
l1->show();
l2->show();
delete bmwfty; bmwfty = NULL;
delete audifty; audifty= NULL;
delete p1, p2; p1= NULL; p2= NULL;
delete l1, l2; l1= NULL; l1= NULL;
}
六、应用场景
一、工厂模式应用到以下场景:
1、日志记录器
我们可以使用工厂方法模式来实现日志记录器,将客户端从具体的日志记录器实现中解耦出来,使得客户端只需要关注创建日志记录器的接口,而不用关心具体的实现方式。在这个场景中,我们可以定义一个抽象的Logger工厂接口,具体的文件日志记录器、数据库日志记录器等都实现这个接口并提供自己的创建方法,客户端可以通过调用Logger工厂接口的方法来创建相应的日志记录器。
2、图片读取器
在项目中,我们经常需要读取不同类型的图片,如bmp、jpeg、png等。使用工厂方法模式,我们可以定义一个抽象的ImageReader工厂接口,具体的BmpReader、JpegReader、PngReader等都实现这个接口并提供自己的创建方法,客户端可以通过调用ImageReader工厂接口的方法来创建相应类型的图片读取器。这样一来,我们可以在不同的图片读取器实现中,对图片的解析方式、压缩方式等进行优化和改进,而不需要修改客户端代码。
3、数据库访问器
在数据库操作中,我们经常需要使用到Connection、Statement、ResultSet等对象。使用工厂方法模式,我们可以定义一个抽象的DbConnector工厂接口,具体的MySqlConnection、OracleConnection、SqlServerConnection等都实现这个接口并提供自己的创建方法,客户端可以通过调用DbConnector工厂接口的方法来创建相应类型的数据库访问器。这样一来,我们可以在不同的数据库访问器实现中,对数据库连接、查询性能等进行优化和改进,而不需要修改客户端代码。
4、加密解密器
在项目中,我们经常需要对敏感数据进行加密和解密,如用户密码、信用卡号等。使用工厂方法模式,我们可以定义一个抽象的Encryptor工厂接口,具体的DesEncryptor、AesEncryptor、RsaEncryptor等都实现这个接口并提供自己的创建方法,客户端可以通过调用Encryptor工厂接口的方法来创建相应类型的加密解密器。这样一来,我们可以在不同的加密解密器实现中,对加密算法、安全性等进行优化和改进,而不需要修改客户端代码。
5、GUI控件
在图形界面程序中,我们经常需要使用各种GUI控件,如按钮、文本框、下拉框等。使用工厂方法模式,我们可以定义一个抽象的WidgetFactory工厂接口,具体的WinWidgetFactory、MacWidgetFactory、LinuxWidgetFactory等都实现这个接口并提供自己的创建方法,客户端可以通过调用WidgetFactory工厂接口的方法来创建相应类型的GUI控件。这样一来,我们可以在不同的GUI控件实现中,对界面风格、交互方式等进行优化和改进,而不需要修改客户端代码。
6、代码生成器
在一些代码生成器的开发中,我们需要根据不同的需求生成不同的代码,如Java代码、C++代码等。使用工厂方法模式,我们可以定义一个抽象的CodeGenerator工厂接口,具体的JavaCodeGenerator、CPlusPlusCodeGenerator等都实现这个接口并提供自己的创建方法,客户端可以通过调用CodeGenerator工厂接口的方法来创建相应类型的代码生成器。这样一来,我们可以在不同的代码生成器实现中,对代码风格、编译性能等进行优化和改进,而不需要修改客户端代码。
二、工厂模式 应用场景
(1)通常在使用word办公软件的时候,会根据需要绘制出饼状图,柱状图,折线图等图形。可以提供一个工厂类,根据用户的选择创建出不同类型的图形。
(2)QQ空间背景样式,博客背景样式等都提供了各种风格的样式。提供一个工厂,根据用户选择的具体风格样式,创建出各个不同的背景风格,用来装饰QQ空间。
(3)网页下载工具的开发: 根据需要可以下载新浪网页、腾讯网页、搜狐网页等。根据用户的选择,把网页类型传进工厂,将下载该类型的网页内容。
(4)淘宝购物最后一个支付环节,可以选择货到付款、网上银行、支付宝等类型支付。用户可以选择具体的支付方式完成订单,这也是简单工厂模式的一种应用。
(5)电影院打折算法: VIP5折、学生票5折、成人票正常收费等打折算法。
(6)多功能计算器的开发:封装加减乘除等运算操作(大话设计模式的例子)
(7)在很多游戏场合,游戏角色可以选择各种各样的武器,如:手枪、AK47、步枪、大刀等。
(8)如果电脑上装有QQ输入法、搜狗输入法、微软拼音输入法,用户可以设置使用哪种类型的输入法。类似的还可以设置IE浏览器、谷歌浏览器、火狐浏览器。可以设置word2003或者金山的WPS。这些都可以理解为简单工厂模式的一种运用。
(9)软件公司决策是否开发哪一种产品,银行卡识别、身份证识别还是驾驶证识别。
(10)生活中也有很多类似的工厂: 富士康代工工厂;安踏加工厂;咖啡生产基地;沃尔玛等超市提供各种产品供用户使用;肯德基马当劳等。