简单工厂、工厂方法和抽象工厂是三种常见的工厂设计模式,它们在软件设计中各有其独特的应用场景和优缺点。因为三种设计模式都属于工厂模式,在实际应用中可能存在误用的场景,这里对其做下对比,以便更好的理解这三种设计模式。
简单工厂
简单工厂模式并不在二十三个标准的设计模式中,可以将简单工厂模式看成工厂方法模式的退化实现。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类,所以也将简单工厂称为静态工厂方法模式。其类图表示如下:
从简单工厂的类图可知,简单工厂仅抽象了Product,而对于创建Product的Factory,则只有一个。
简单工厂模式适用于需要创建的Product比较少,且基本不会变化的情况。与其他的创建型模式一样,简单工厂将产品的创建从产品中分离出来,实现了职责分离。使用简单工厂可以减少工厂子类的创建,实现一定程度的简易性。但是简单工厂违反了面向对象设计的开闭原则。当需要增加新的Product时,需要修改工厂类的现有判断逻辑。在产品类型较多时,会导致工厂类演变成上帝类,同时,会造成工厂逻辑过于复杂,不利于系统的扩展。
工厂方法
工厂方法模式是最常用的工厂模式,通过定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式将类的实例化(具体Product的创建)延迟到工厂类的子类(具体工厂)中完成,即由子工厂类来决定该实例化哪一个类。其类图表示如下:
工厂方法模式适用于Product的子类较多且无法预知确切类型,或需要扩展软件库或框架中对象的创建等场景。与其他的创建型模式一样,工厂方法将创建产品的代码与实际使用产品的代码分离,实现了职责分离,避免了产品创建和实际产品之间的紧耦合。相比简单工厂,工厂方法在抽象Product的基础上,也对Factory进行了抽象,并要求每个Factory子类负责某一类Product的创建。这样,产品创建代码放在单独的类里,从而使得代码更容易维护,符合单一职责原则。当需要引入新的Product子类时,同步创建新的Factory子类,无需更改客户端调用代码,符合开闭原则。但是工厂方法解耦的设计,也带来代码复杂度上升的问题。因为需要为每个Product创建一个Factory子类,所以工厂模式会随着Product子类的增加,而同步增加Factory子类。针对这个问题,最好的情况是将该模式引入产品类的现有层次结构中(将工厂类组合到产品类里)。
抽象工厂
抽象工厂模式是所有工厂模式中最复杂的模式,通过提供一个创建一组相关或相互依赖的对象的接口,让每个子类生产一系列相关的Product。其类图表示如下:
抽象工厂适用于需要创建的多个不同系列的相关产品存在交互且无法预知对象确切类别及其依赖关系,或需要扩展软件库或框架中对象的创建等场景。与其他的创建型模式一样,抽象工厂将创建产品的代码与实际使用产品的代码分离,实现了职责的分离,避免产品创建和实际产品之间的紧耦合。与工厂方法一样,抽象工厂也对Factory进行了抽象,并要求每个Factory子类负责某一类Product的创建。这样,产品创建代码放在单独的类里,从而使得代码更容易维护,符合单一职责原则。当需要引入新的Product子类时,同步创建新的Factory子类,无需更改客户端调用代码,符合开闭原则。但是,相比工厂方法中每个Factory负责一个Product的创建,抽象工厂中的Factory负责一个产品系列相关产品的创建。抽象工厂解耦的设计,会带来比工厂方法更高的代码复杂度问题。在设计良好的程序中,每个类仅负责一件事。如果一个类与多种类型产品交互,就可以考虑将工厂方法抽取到独立的工厂类或具备完整功能的抽象工厂类中。在实际的开发中,应首先考虑工厂方法,如果工厂方法仍无法满足需求,再考虑使用抽象工厂。
简单工厂、工厂方法、抽象工厂对比
无论简单工厂、工厂方法,还是抽象工厂,都是工厂模式,都是创建型模式。其主要职责都是将创建产品的代码与实际使用产品的代码分离,实现了职责分离,避免了产品创建和实际产品之间的紧耦合。相比工厂方法和抽象工厂,简单工厂没有对工厂进行抽象,而是将所有产品的创建都放在一个工厂里实现。虽然简单工厂可以减少子类工厂的创建,保证一定程度的代码的简易性。但是这样的设计模式是不符合面向对象设计的开闭原则。当需要增加新的Product时,需要修改现有工厂的判断逻辑。相比简单工厂,工厂方法和抽象工厂则增加了对工厂的抽象,将产品的创建分配到工厂子类。这样,产品创建的代码放在单独的子类里,使得代码更容易维护,符合单一职责原则。当需要引入新的Product子类时,同步创建新的Factory子类,无需更改客户端调用代码,这符合开闭原则。但是,工厂方法和抽象工厂因引入工厂子类,无疑也会带来代码复杂度上升的问题。对于工厂方法来说,每新增一个Product子类,就需要同步创建一个Factory子类。而对抽象工厂来说,每新增一个Product系列,就需要同步创建一个Factory子类。所以工厂模式或抽象工厂会随着Product子类或Product系列的增加,会同步增加Factory子类。
在实际的应用中,对于产品子类变化比较少的场景,可以先使用简单工厂,这样可以保证代码的简易性。当产品子类不断增加时,为了更好的隔离变化,需要为每个产品增加对应的工厂。如果产品种类进一步增加,则考虑使用抽象工厂。在工厂方法和抽象工厂的选取上,应优先选择工厂方法,如果工厂方法不能很好的隔离变化,则应进一步考虑使用抽象工厂。
参考
https://yiyan.baidu.com/ 文心一言
《设计模式:可复用面向对象软件的基础》 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides 著 李英军, 马晓星 等译
https://zhuanlan.zhihu.com/p/158861140 工厂模式-简单工厂、工厂方法、抽象工厂解析
https://cloud.tencent.com/developer/article/2342470 设计模式学习笔记(三)简单工厂、工厂方法和抽象工厂之间的区别