1、简介
工厂设计模式,可能是我们开发过程中无形之中使用的最多的设计模式。工厂设计模式包括简单工厂(Simple Factory)、方法工厂(Method Factory)、抽象工厂(Abstract Factory),其中简单工厂设计模式并包不含在GOF23种设计模式之中,但是其使用也十分广泛;这三种设计模式之间存在一定的关系,层层递进;但是三种工厂设计模式各自有各自适用的场景,在实际开发中选择设计模式应该深思熟虑。
三种工厂设计模式之间的关系类比图如下:
2、大纲
本文围绕简单工厂(Simple Factory)、方法工厂(Method Factory)、抽象工厂(Abstract Factory)三种设计模式开展,与大家共同学习。
大纲图如下:3、简单工厂
3.1 说明
简单工厂模式(Simple Factory)是一个可以生成不同产品的类,我认为可以类比为一个工具类,用于将多个产品的创建聚合到一起。本文的开展介绍将围绕我华为、小米、苹果三个系列的产品开展。(李子捌支持国产,但也兼容并包,我们时刻保持学习,努力追赶超越,国产加油!)
简单工厂UML图示
3.2 使用场景
简单工厂主要适合产品类较少、设计上能固定个数的时候,我们通过简单工厂来获取对象,屏蔽对象生成的具体细节。简单工厂代码耦合,不符合开闭原则,不适合过多产品(实现类)的场景下使用。
3.3 使用举例
如下例子创建了如下几个接口与类的层级关系
移动手机接口IMobilePhone
华为手机HuaweiPhone、小米手机XiaomiPhone、苹果手机IPhone分别实现了IMobilePhone接口,并重写其中的方法抽象
简单工厂MobilePhoneFactory用于根据客户端(client)的调用传参创建并返回指定的手机实例对象
PhoneTypeEnum是一个枚举类,用于客户端传参,可以字符串等常量代替均可
3.3.1 移动手机接口IMobilePhone代码示例
3.3.3 简单工厂代码示例
客户端在调用简单工厂生产所需产品实例时,需要入参用于区分实例化哪个具体的产品。如下展示的枚举方式:
package com.liziba.pattern.factory.simpleFactory; import com.liziba.pattern.factory.IMobilePhone; import com.liziba.pattern.factory.HuaweiPhone; import com.liziba.pattern.factory.IPhone; import com.liziba.pattern.factory.XiaomiPhone; /** * <p> * 手机简单工厂 * </p> * * @Author: Liziba * @Date: 2021/6/28 21:14 */ public class MobilePhoneFactory { public static IMobilePhone getMobilePhone(PhoneTypeEnum phoneType) { IMobilePhone phone = null; switch (phoneType) { case HUAWEI: phone = new HuaweiPhone(); break; case XIAOMI: phone = new XiaomiPhone(); break; case IPHONE: phone = new IPhone(); break; default: break; } return phone; } /** * 枚举类用于区分手机类型,本应写出去,为了减少类的示例个数内置于简单工厂中 * 也可以用字符串或者其他常量代替均可(但是我不推荐这种) */ enum PhoneTypeEnum { HUAWEI("华为", "A"), XIAOMI("小米", "B"), IPHONE("苹果", "C"); private String name; private String value; PhoneTypeEnum(String name, String value) { this.name = name; this.value = value; } } }
3.5 优缺点总结
简单工厂模式优点
代码编码简单
调用方清晰明了
在一定程度上区分了产品和生产产品工厂之间的职责
简单工厂模式缺点
工厂职责不单一,能创建各种产品,理论上不符合单一职责原则(当然这个单一职责的职责区分界限视情况而定)
工厂代码耦合,新增产品会导致代码的修改,理论上不符合开闭原则。新增产品需要修改简单工厂类。这点符合前面说的,简单工厂模式在产品少或者产品个数能确定的场景使用最佳。
4、工厂方法
4.1 说明
工厂方法设计模式(Factory Method)也为虚拟构造函数(Virtual Constructor),我觉得老外这个Virtual Constructor称呼还挺有那个意思的;工厂通过实现一个统一个工厂接口,来约定生成何种产品。工厂方法将具体产品的生成推迟到了子类中,本身只约束不生产。这种方式解决了简单工厂不符合开闭原则的缺点。
工厂方法(Factory Method)其原文定义如下:
Define an interface for creating an object, but let subclassed decide which class to instantiate. Factory Method lets a class defer instantiation to subclassed.
工厂方法UML图示
4.2 使用场景
工厂方法由于其符合,开闭原则,在产品(实现类)个数不确定的情况下,使用该场景代码的可用性更强,其主要使用场景如下:
产品(类)无法预测其具体实现或实现的个数(类的个数多)
具体实现需要交给子类处理,父类只提供约束规范(实现很多,后期可能会一直加,或者当前不能全部穷举)
客户端的调用对于产品的创建(对象的实例化)可以透明,无需知道具体细节
4.3 使用举例
在简单工厂中移动手机接口IMobilePhone及其实现类华为手机HuaweiPhone、小米手机XiaomiPhone、苹果手机IPhone沿用,下面代码将不在重复演示,其创建了如下几个接口与类的层级关系:
移动手机接口IMobilePhone
华为手机HuaweiPhone、小米手机XiaomiPhone、苹果手机IPhone分别实现了IMobilePhone接口,并重写其中的方法抽象
工厂方法接口IMakePhoneFactory,提供创建手机的抽象方法
华为手机工厂HuaweiPhoneFactory、小米手机工厂XiaomiPhoneFactory、苹果手机工厂IPhonePhoneFactory分别实现了IMakePhoneFactory接口,并重新了其中的抽象方法,分别创建各自的手机实例。
4.3.1 IMobilePhone、HuaweiPhone、XiaomiPhone、IPhone代码示例
在源码org.slf4j.ILoggerFactory可以查看其两个实现类,类关系图和部分相关代码在下面展示:
/** * ILoggerFactory */ public interface ILoggerFactory { Logger getLogger(String var1); } /** * NOPLoggerFactory */ public class NOPLoggerFactory implements ILoggerFactory { public NOPLoggerFactory() { } public Logger getLogger(String name) { return NOPLogger.NOP_LOGGER; } } /** * SubstituteLoggerFactory */ public class SubstituteLoggerFactory implements ILoggerFactory { // ... public synchronized Logger getLogger(String name) { SubstituteLogger logger = (SubstituteLogger)this.loggers.get(name); if (logger == null) { logger = new SubstituteLogger(name, this.eventQueue, this.postInitialization); this.loggers.put(name, logger); } return logger; } // ... }
4.5 优缺点总结
工厂方法模式优点
一个产品对应一个工厂,符合单一职责原则
易于扩展,新增产品只需新增一个工厂和相关产品即可,无需改动以前代码,符合开闭原则
屏蔽对象创建细节,客户端调用清晰
工厂方法模式缺点
产品数目过多时,会导致类的个数成倍增长
抽象程度高,难以理解,对开发开发要求较高
工厂方法在某些需要一个工厂生产多种产品的情况下显得乏力
5、抽象工厂
5.1 说明
前面有说道,简单工厂就像民间个体作坊,工厂方法就像小型加工厂,而抽象工厂就像大型代工厂。抽象工厂能解决工厂方法中一个工厂无法生产多个产品的问题。
抽象工厂(Abstract Factory),简单来说就是一个工厂的工厂,它将单个相关/依赖的工厂组合在一起而不指定它们的具体类的工厂。
抽象工厂(Abstract Factory)其原文定义如下:
Provide an interface for creating familiesof related or dependent objects without specifying their concrete classes.
抽象工厂UML图示
5.2 使用场景
在介绍抽象工厂的使用场景之前,我们先来介绍一个概念,产品族与产品等级结构。
我们知道华为、小米、苹果公司都不仅仅只生产手机;其也生产电脑、平板等各种产品。在上述描述的各种产品中,华为手机、小米手机与苹果手机就是一个产品等级,而华为手机、华为电脑、华为平板就构成了一个产品族。我们通过华为、小米和苹果的各种产品以一张图来示例这二者的关系:
在这张图中,五边形代表手机、圆形代表平板、三角形代表电脑;横坐标手机、平板、电脑分别代表三个不同的产品等级(例如橙色部分包含的三个三角形);纵坐标华为、小米、苹果各自的三种产品组合在一起称为产品族(例如绿色部分包含的一组五边形、原型和三角形)。清晰了这个概念我们就能大致的理解抽象工厂的使用场景也为后续举例加深映像做了铺垫。其使用场景如下:
系统需要配置多个系列的产品,并且它们约定一起使用(产品族)
只提供给客户端调用接口,不暴露具体实现
产品和产品族之间具有一定的一致性约束,通过接口规范来实现
5.3 使用举例
在简单工厂中移动手机接口IMobilePhone及其实现类华为手机HuaweiPhone、小米手机XiaomiPhone、苹果手机IPhone沿用,下面代码将不在重复演示,手机在这里是一个产品等级。此外新增电脑产品等级,其代码实现如下。
5.3.1 手机产品等级代码示例
ILaptop的三个实现类:
5.4 优缺点总结
抽象工厂优点
抽象工厂能够很好的适配产品族的使用场景,给客户端的调用带来了极大的便利,解决了工厂方法的短板
抽象工厂代码设计符合整体上开闭原则(但是其实不符合这个界限也很微妙,看各位大神么自己去抉择)
抽象工厂缺点
接口与类的设计较为复杂
产品族中新增产品需要修改上层接口(这种情况也是有的),所以他也不是很符合开闭原则。
如果您真的阅读到了这一行,我深深的对您表达我的致意。文章中出现的错误请多多指教,李子捌一定及时修正。如您不嫌弃,高抬贵手给个三连,先行谢过。