工厂模式在实际开发中比较常见,工厂模式有简单工厂模式,工厂方法模式,抽象工厂模式,都属于创建型模式,其中工厂方法模式是我们一般情况下说的工厂模式。创建型模式指的是,能够将类的实例化过程进行抽象,将对象的创建和对象的使用使用过程进行分离的模式,所以除了工厂模式之外,有部分其他的设计模式也属于创建型模式。
简单工厂模式
简单工厂模式又称静态工厂方法模式,作用是:可以根据传入参数的不同,创建出不同的类的实例,此处的创建者,即工厂,这些被创建出的类的实例,都继承自同一个抽象产品类。UML图如下:
productA和productB都继承自抽象类product,工厂类factory根据传入的string确定要返回的产品类的实例。在UML图里,类与类之间的不同关系用不同的线条表示,这里可以看到,虚线的空闲三角指向代表的是继承抽象类;虚线的线条三角表示的是运行时的依赖关系,factory类在运行期间会依赖两个具体的产品类。简单工厂模式的代码实现:
//抽象类
class product
{
//包含纯虚函数和某些公共的方法和属性
virtual void use() = 0;
};
//产品A
class productA : public product
{
//实现继承的纯虚函数以及添加属于自己的某些方法和属性
void use();
};
//产品B
class productB : public product
{
//实现继承的纯虚函数以及添加属于自己的某些方法和属性
void use();
};
//工厂类
class factory
{
//提供一个创建产品的方法
product* createProduct(string type)
{
if(type == "A")
{
return new productA();
}
else if(type == "B")
{
return new productB();
}
else
return NULL;
}
};
优缺点和适用场景
优点:
1.客户端不必关心创建细节,只需要知道想要创建的实例类型,传入到工厂类的创建方法即可。
2.实际开发中会存在一些本质相同,只有显示不同的功能,这样的话无需修改代码,修改配置即可实现。
缺点:
1.工厂类集中了所有产品的创建,一旦出了bug,整个系统都会有问题。
2.工厂的创建方法在每一次添加新的产品时都要再次修改。
适用场景:
1.工厂类负责创建的对象比较少的情况,此时代码中的类也不会太多。
2.客户端只知道传入工厂类的参数,对于如何创建对象不关心。
工厂方法模式
工厂方法模式即工厂模式,在简单工厂模式的基础上让工厂类成为一个抽象类,具体的工厂类都继承该抽象工厂。
在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,所有的工厂子类都继承自同一个工厂父类,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。代码表达:
//抽象工厂类
class Factory
{
virtual Product* FactoryMethord();
//其他的公共属性以及方法
...
};
//抽象产品类
class Product
{
virtual void use();
//其他公共属性和方法
...
};
//具体的产品类
class ConcreteProduct
{
void use();
//其他子类的属性和方法
...
};
//具体的工厂类
class ConcreteFactory
{
Product* FactoryMethord()
{
return new ConcreteProduct();
}
};
//主函数的使用
int main()
{
Factory * fc = new ConcreteFactory();
Product * prod = fc->factoryMethod();
prod->use();
delete fc;
delete prod;
return 0;
}
到这里会有一个疑问,工厂方法模式和简单工厂模式看起来结果是一样的,而且工厂方法模式还使用了抽象的工厂父类,比起简单工厂模式还复杂了,有必要吗?实际上,工厂方法模式一般不会只有一个具体的工厂类,这里的UML图和代码只写了一个而已,如果工厂方法模式仅仅返回一个具体产品对象,便违背了工厂方法的用意,退化成了简单工厂模式。
优缺点及适用场景
优点:
1.调用者只需知道产品类对应的工厂类是哪个,不需要关心具体的创建细节。
2.工厂可以自主决定确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。
3.加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
缺点:
1.系统的类数量变多,因为每增加一种类型的产品,需要对应增加一个工厂类和产品类。
2.引入了抽象层,增加了系统的抽象性和理解的难度,实现上也比简单工厂模式复杂。
适用环境:
1.一个类不知道它所需要的对象类,但是知道创建这个类所需要的工厂类。
2.一个类通过其子类来指定创建哪个对象
3.将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
抽象工厂模式
抽象工厂模式是需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式。
每一个具体的工厂类可以创建多个同种类产品实例,这里可以这么理解:ProductA是鼠标,ProductB是显示器,1表示品牌为联想,2表示品牌为戴尔,所以,ConcreteFactory1能一次性创建出联想品牌的显示器和鼠标,ConcreteFactory2能一次性创建出戴尔品牌的显示器和鼠标,是不是很方便?代码体现:
//抽象类和具体类的实现都类似于前面的,这里只体现具体使用
int main()
{
Factory* ft1 = new ConcreteFactory1();
ProductA pda1 = ft1->createProductA();
ProductB pdb1 = ft1->createProductB();
pda1->click();
pdb1->look();
Factory* ft2 = new ConcreteFactory2();
ProductA pda2 = ft2->createProductA();
ProductB pdb2 = ft2->createProductB();
pda2->click();
pdb2->look();
return 0;
}
优缺点及适用场景
优点:
1.高内聚低耦合,隔离具体类的生成,客户端并不需要知道具体工厂创建了什么,基于此只需修改具体工厂,即可改变系统行为
2.符合开闭原则,增加新的产品族很方便,不需要修改现有的系统,新增具体工厂就可以了。(eg:再新增一个品牌为华硕的显示器和鼠标)
缺点:
新增一个新的产品等级结构麻烦,需要修改抽象工厂类和具体工厂类。(eg:每个品牌新增一个键盘类)
适用环境:
1.一个产品族中的多个对象被设计成一起工作,需要根据当前环境来决定其行为的软件系统。
2.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
3.系统中有多于一个的产品族,而每次只使用其中某一产品族。
感悟
设计模式的学习在有了一定的工作经验之后会理解的更加深刻,在学习的时候能够自己画画模式的UML类图,写写形成模式的简单代码,有助于理解和记忆。
参考:https://design-patterns.readthedocs.io/zh_CN/latest/index.html