前言
设计模式的本质在于抽象、解耦,用抽象来隔离变化。将复杂的事务按照六大设计原则,分解成一个个单一职责的个体。换而言之,是个体的高内聚和简单化,然后再组合到一起完成职能。合理使用设计模式,可以使程序设计更加标准化、代码编制更加工程化,使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
好看的代码千篇⼀律,需要多加练习,才能⼈⻋合⼀,站在设计模式的基础上构建出更加健壮的代码。上一篇文章中,我们已经聊完了GOF23中的第一个模式——单例模式,如果没有看过的,可以回顾一下。
创建型模式的工作原理
创建型模式提供了一种创建对象的机制,抽象实例化的过程,隐藏了对象的创建细节,对外只提供一个通用接口,能够提升已有代码的灵活性和可复⽤性。创建型模式有五种:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。本文带着大家重新学习第二种设计模式——工厂方法模式。
工厂方法模式
定义
⼯⼚模式⼜称⼯⼚⽅法模式。在该模式中,工厂父类负责提供创建产品对象的公共接口,而工厂子类负责生成具体的产品对象,换而言之,调用方只需要知道产品的类名或者某个标识就可以了,不需要知道产品对象的详细创建过程,将具体类的实例化操作延迟到工厂子类中完成,降低模块之间的耦合性。提供代码结构的扩展性,屏蔽每⼀个功能类中的具体实现逻辑,减少开发、维护的成本。
结构
通用的工厂模式一般包含四个角色:
1、Factory:抽象工厂类
public abstract class Factory {
/**
* 创建产品对象
*
* @param clazz
* @param <T>
* @return
*/
public abstract <T> T createProduct(Class<T> clazz);
}
2、ConcreteFactory:具体工厂类,负责实现创建所有产品的内部逻辑,该工厂类可以被直接调用,创建所需的具体对象。
public class ConcreteFactory extends Factory {
@Override
public <T> T createProduct(Class<T> clazz) {
Product product = null;
try {
product = (Product) Class.forName(clazz.getName()).getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) product;
}
}
3、Product:抽象产品类,工厂类所创建的所有产品的父类,封装了产品对象的公共方法
public abstract class Product {
public void commonMethod() {
//公共方法
}
//实例方法
public abstract void method();
}
4、ConcreteProduct:具体产品类
public class ConcreteProduct extends Product {
@Override
public void method() {
//业务逻辑处理...
}
}
具体的调用方法:
public static void main(String[] args)
{
Factory factory = new ConcreteFactory();
Product product = factory.createProduct(ConcreteProduct.class);
//后续的业务逻辑
}
模式分析
简单工厂模式:实例化对象的时候不再使用 new Object()的形式,根据用户选择的产品类名或者某个标识来实例化具体的类
工厂方法模式:工厂方法模式是对简单工厂模式进一步的解耦,在工厂方法模式中是一类产品对应一个工厂类,而这些工厂类都继承于一个抽象工厂。这相当于是把原本会随着业务扩展而庞大的简单工厂类,拆分成了一个个的具体产品工厂类,这样代码就不会都耦合在同一个类里。
优点
1、良好的封装性,在工厂方法模式中,工厂方法用来创建业务所需要的产品,同时还向客户隐藏了具体产品的创建细节,只需要关心所需产品对应的工厂,降低了模块间的耦合性。
2、良好的扩展性,当系统中需要加入新产品时,无须修改抽象工厂和抽象产品提供的接口,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以完成系统的扩展。
3、屏蔽产品类,这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化。
缺点
当需要新增产品时,都必须要编写新的具体产品类,而且还要提供与之对应的具体工厂类,随着类的不断增加,在一定程度上增加了系统的复杂度,会有更多的类需要编译和加载,会给系统带来一些额外的开销。
最佳实践
1、工厂方法模式是手动new一个对象的替代品,在需要创建对象的地方都可以使用该模式。使用之前,需要考虑是否有必要增加工厂类进行管理,增加代码的复杂度。
2、当业务上需要灵活的、可扩展的功能时,可以考虑采用工厂方法模式。万物皆对象,提前规避功能扩展引发的代码重构。
3、一个类有多个子类,并通过其子类来指定创建对象,可以使用工厂方法模式。对于抽象工厂类只需要提供一个创建产品的接口,由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,实现子类对象覆盖父类对象。
模式扩展
1、在抽象工厂角色中可以定义多个具体工厂角色,不同的具体工厂角色可以包含不同的业务逻辑,以满足对不同的产品对象的创建需求。
2、产品对象在调用完之后,不会立刻清理,而是将已经创建过的产品保存到一个集合中(如数组、List、Map等)。当用户下一次发起生成产品对象的请求时,会在集合中进行查询。如果有满足要求的产品对象,就直接将该产品返回客户端;如果集合中没有符合要求的产品对象,那么就创建一个新的满足要求的产品对象,然后将这个对象在增加到集合中,再返回给客户端。
3、如果工厂角色仅只能创建一种具体产品对象,便违背了工厂方法的用意,发生退化,此时就不再是工厂方法模式了。一般来说,工厂对象应当有一个抽象的父类型,如果抽象工厂类只有一个具体工厂类的话,抽象工厂就可以省略,也将发生了退化。当只有一个具体工厂,在具体工厂中可以创建所有的产品对象,并且工厂方法设计为静态方法时,工厂方法模式就退化成简单工厂模式。
总结
工厂模式理解起来不难,但不是每个人都能用好。工厂方法模式还可以与其他模式混合使用(例如模板方法模式、单例模式、策略模式等),熟能生巧,对设计模式的理解加深,才能得心应手。