前言
工厂模式(Factory Pattern)是最常用的设计模式之一,它与之前介绍的『Design Pattern: Singleton』都属于创建类型的设计模式。
它在创建对象时提供了一种封装机制,将实际创建对象的代码与使用代码分离。在创建对象时不会对客户端暴露创建逻辑,而是使其创建过程延迟到子类进行,并且通过一个共同的接口来指向新创建的对象。它主要解决接口选择的问题。
一般情况下,工厂模式有三种实现:简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)。 这三种模式从上到下逐步抽象,不过值得一提的是,简单工厂模式并不在 GoF 23 种设计模式之列。
下面我们逐一介绍这三种实现。
实现
假设我们需要生产两款手机(以 Apple 和 Samsung 为例),在不使用设计模式的情况下,我们可能会这么写:
class Apple(val screen: String, val usb: String) {
fun hardware() = println("[Apple] screen: $screen, usb: $usb")
}
class Samsung(val screen: String, val usb: String) {
fun hardware() = println("[Samsung] screen: $screen, usb: $usb")
}
fun main() {
val apple = Apple("LCD", "Lightning")
apple.hardware()
val samsung = Samsung("OLed", "Type-C")
samsung.hardware()
}
即由用户手动去创建实际的对象,在上面这个例子中,用户需要知道如何创建一台手机,需要知道什么手机使用什么屏幕和什么类型的 USB 插口,用户和手机的生产就耦合在一起了。
为了降低耦合性,我们引入工厂模式,把手机的生产细节放到工厂里,用户无需关心这台手机使用什么屏幕和 USB 插口,直接调用工厂的创建方法,不必知道创建的细节。
Simple Factory 简单工厂模式
简介
简单工厂模式又叫作静态工厂方法模式(Static Factory Method)。它将对象的创建逻辑封装在一个工厂类中,客户端通过调用工厂类的静态方法来创建对象。
简单工厂模式包含如下角色:
- Factory:工厂角色,负责实现创建所有实例的内部逻辑。
- AbstractProduct:抽象产品角色,是创建的所有对象的父类,负责描述所有实例的公共接口。
- Product:具体产品角色,即创建目标,所有创建的对象都是该角色。
abstract class Phone {
abstract fun hardware()
}
class Apple(val screen: String, val usb: String) : Phone() {
override fun hardware() = println("[Apple] screen: $screen, usb: $usb")
}
class Samsung(val screen: String, val usb: String) : Phone() {
override fun hardware() = println("[Samsung] screen: $screen, usb: $usb")
}
object PhoneFactory {
fun createPhone(type: String): Phone? {
return when (type) {
"Apple" -> Apple("LCD", "Lightning")
"Samsung" -> Samsung("OLed", "Type-C")
else -> null
}
}
}
fun main() {
val apple: Phone? = PhoneFactory.createPhone("Apple")
apple?.hardware()
val samsung: Phone? = PhoneFactory.createPhone("Samsung")
samsung?.hardware()
}
在这个例子中,Phone
就充当了抽象产品角色,Apple
和 Samsung
都是具体产品,工厂提供了静态方法用于创建具体产品,用户只需要传入手机的品牌即可获取手机实例。
优缺点
- 优点:简单工厂模式提供专门的工厂类用于创建对象,实现了对象创建和使用的职责分离,客户端不需知道所创建的具体产品类的类名以及创建过程,只需要知道具体产品类所对应的参数即可。通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
- 缺点:不符合设计模式的开闭原则(对扩展开放,对修改关闭),每次添加新的产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护。
Factory Method 工厂方法模式
简介
工厂方法模式又被称为虚拟构造子模式(Virtual Constructor)或者多态工厂模式(Polymorphic Factory)。工厂方法模式是目标是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。
工厂方法模式包含如下角色:
- AbstractFactory:抽象工厂角色,与应用程序无关,主要在创建模式中规范和产品对应的工厂对象的标准化定义。
- Factory:具体工厂角色,和用户直接交互的具体实现,用户创建产品对象。
- AbstractProduct:抽象产品角色,是创建的所有对象的父类,负责描述所有实例的公共接口。
- Product:具体产品角色,即创建目标,所有创建的对象都是该角色。
abstract class Phone {
abstract fun hardware()
}
class Apple(val screen: String, val usb: String) : Phone() {
override fun hardware() = println("[Apple] screen: $screen, usb: $usb")
}
class Samsung(val screen: String, val usb: String) : Phone() {
override fun hardware() = println("[Samsung] screen: $screen, usb: $usb")
}
interface PhoneFactory {
fun createPhone(): Phone
}
class AppleFactory : PhoneFactory {
override fun createPhone(): Phone = Apple("LCD", "Lightning")
}
class SamsungFactory : PhoneFactory {
override fun createPhone(): Phone = Samsung("OLed", "Type-C")
}
fun main() {
val factory: PhoneFactory = AppleFactory()
val phone: Phone = factory.createPhone()
phone.hardware()
}
工厂方法模式在简单工厂模式的基础上抽象了工厂类,即将具体的手机创建过程交给专门的工厂子类去完成。
工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
优缺点
- 优点:最直接的优点就是在满足开闭原则的基础上实现功能的扩展,核心工厂类不再负责所有产品的构建,而是将具体的工作交给工厂子类实现,需要增加新产品时,无须修改已有的抽象或具体的工厂或产品,只要添加一个具体工厂和具体产品就可以。
- 缺点:由于在添加新产品时需要编写新的产品类和工厂类,系统中类的个数将成对增加,在一定程度上增加了复杂度,有更多的类需要编译和运行,也会给系统带来一些额外开销。
Abstract Factory 抽象工厂模式
简介
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
为了更清晰地理解工厂方法模式,需要先引入两个概念:
- 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、索尼电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
抽象工厂模式包含如下角色:
- AbstractFactory:抽象工厂角色,与应用程序无关,主要在创建模式中规范和产品对应的工厂对象的标准化定义。
- Factory:具体工厂角色,用于生产不同的产品族。
- AbstractProduct:抽象产品角色,定义了产品的规范,描述了产品的共同主要特性和功能,抽象工厂模式有多个抽象产品。
- Product:具体产品角色,由专门的具体工厂来创建,具体产品和具体工厂之间往往一一对应。
interface Tablet {
fun play()
}
interface Earphone {
fun play()
}
class iPad : Tablet {
override fun play() = println("Apple iPad play game")
}
class Tab : Tablet {
override fun play() = println("Samsung Tab play game")
}
class AirPods : Earphone {
override fun play() = println("Apple AirPods play music")
}
class Buds : Earphone {
override fun play() = println("Samsung Buds play music")
}
interface Factory {
fun createTablet(): Tablet
fun createEarphone(): Earphone
}
class AppleFactory : Factory {
override fun createTablet(): Tablet = iPad()
override fun createEarphone(): Earphone = AirPods()
}
class SamsungFactory : Factory {
override fun createTablet(): Tablet = Tab()
override fun createEarphone(): Earphone = Buds()
}
fun main() {
val appleFactory: Factory = AppleFactory()
appleFactory.createTablet().play()
appleFactory.createEarphone().play()
val samsungFactory: Factory = SamsungFactory()
samsungFactory.createTablet().play()
samsungFactory.createEarphone().play()
}
这里定义了平板和耳机两类产品,各为一个产品等级结构,Apple 和 Samsung 都生产这两类产品,所以旗下的产品可视为产品族
抽象工厂可以通过多态,来动态设置不同的工厂,生产不同的产品,同时每个工厂中的产品又不属于同一个产品等级结构。
优缺点
- 优点:可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。增加新的具体工厂和产品族很方便,因为一个具体的工厂实现代表的是一个产品族,无须修改已有系统,符合开闭原则。
- 缺点:开闭原则的倾斜性。增加新的工厂和产品族容易,增加新的产品等级结构却比较麻烦,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。