1.什么是创建型模式
创建型模式顾名思义,是一种专注于类的实例化过程的设计模式,它的目标是将创建对象和使用对象的代码分离。外界只需要知道可以通过一组接口来获取对象,而不用关心具体是如何实现的
创建型模式主要以工厂模式为主,这也是本文研究的重心
2.简单工厂模式
想象有一组按钮ButtonA,ButtonB... ...,他们继承自相同的基类Button,实现了规定的接口。简单工厂模式会提供一个类来创建这些按钮的实例,接口调用者只需要提供不同的参数,就可以获得不同的实例
类图如下所示
部分实现代码如下
Factory.cpp
#include "Factory.h" #include "ButtonA.h" #include "ButtonB.h" Factory::Factory(){} Factory::~Factory(){} Button* Factory::createButton(string bname) { if(bname=="A") return new ButtonA(); else if(bname=="B") return new ButtonB(); return NULL; }
main.cpp
int main(int argc, char** argv) { Button* b=Factory::createButton("B"); b->showType(); delete b; return 0; }
3.简单工厂模式的优缺点和适用场景
优点:
①对象的创建和使用完全分开了,耦合度低。接口的调用者只需要简单的填入参数,由工厂类进行逻辑判断和创建对象
②比较灵活,增加新类或修改、替换类,无需改动接口调用者的代码
缺点:
①工厂类的职责重,出现错误很显然会影响整个系统
②增加新类或替换类需要改动工厂类的逻辑,违背开闭原则
③随着类的增长,工厂类内部的逻辑判断也会越来越复杂
简单工厂模式对接口调用者来说使用方便,很适合要创建的类种类较少的情况,JDK类库中广泛使用了简单工厂模式。
4.简单工厂模式改进:工厂方法模式
在简单工厂模式中,我们利用了Button的多态,通过Button类型的返回值来得到不同的实例,通过统一实现的接口进行使用
现在把多态引入工厂类就可以对简单工厂模式进行改进:创建一个抽象的工厂类,规定接口,由具体的工厂子类来实现接口,创建具体的对象。用户只需要知道对象对应的工厂子类即可
类图如下
对应代码如下
FactoryA.cpp
#include "Factory.h" #include "ButtonA.h" FactoryA::FactoryA(){} FactoryA::~FactoryA(){} Button* FactoryA::createButton() { return new ButtonA(); }
main.cpp
#include <iostream> #include "Button.h" #include "FactoryB.h" using namespace std; int main(int argc, char** argv) { FactoryB* f=new FactoryB() Button* b=f->createButton(); b->showType(); delete b; return 0; }
工厂方法模式保留了简单工厂模式的优点:对接口的调用者来说,获取对象依然很简单(只需要记住参数->只需要记住具体的工厂子类),也无需修改自己的代码。同时,它克服了简单工程模式最大的缺点,增加新类或替换类无需修改客户端代码,只需要增加对应的工厂子类即可,符合开闭原则。核心的父类只定义接口,职责分散。系统拥有了良好的可扩展性。
工厂方法模式并不是完美的,在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
在以下情况下可以使用工厂方法模式:
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。