设计模式 - 抽象工厂模式
实例
手机软件初始化
假设一个手机软件初始化的应用场景,该软件支持IOS
、Android
、Windows
等多个操作系统平台,针对不同的操作系统,该软件提供了不同的操作功能(Operation
)类和操作界面(Surface
)类,并提供相应的工厂类来封装这些类的初始化过程。软件要求具有较好的扩展性以支持新的操作系统平台
- 软件结构示意图如下:
工厂方法模式解决方案
- 初始解决方案采用工厂方法模式,大致如下:
Operation.java
/**
* @Description 操作功能抽象类
*/
public abstract class Operation {
public abstract void init();
}
AndroidOperation.java
/**
* @Description Android操作功能产品类
*/
public class AndroidOperation extends Operation {
@Override
public void init() {
System.out.println("初始化Android操作功能");
}
}
IOSOperation.java
/**
* @Description IOS操作功能产品类
*/
public class IOSOperation extends Operation {
@Override
public void init() {
System.out.println("初始化IOS操作功能");
}
}
OperationFactory.java
/**
* @Description 操作功能工厂类(工厂方法模式)
*/
public interface OperationFactory {
Operation getOperation();
}
AndroidOperationFactory.java
/**
* @Description Android操作功能工厂类
*/
public class AndroidOperationFactory implements OperationFactory {
@Override
public Operation getOperation() {
return new AndroidOperation();
}
}
IOSOperationFactory.java
/**
* @Description IOS操作功能工厂类
*/
public class IOSOperationFactory implements OperationFactory {
@Override
public Operation getOperation() {
return new IOSOperation();
}
}
Test.java
/**
* @Description 工厂方法模式测试类
*/
public class Test {
public static void main(String[] args) {
OperationFactory operationFactory = new AndroidOperationFactory();
Operation operation = operationFactory.getOperation();
operation.init();
operationFactory = new IOSOperationFactory();
operation = operationFactory.getOperation();
operation.init();
}
}
- 输出如下:
初始化Android操作功能
初始化IOS操作功能
- 操作界面抽象代码类似,不一一粘贴,完整类图如下所示:
- 通过类图不难看出,此设计方案当需要支持新的操作系统平台时虽不需要修改现有代码,但类的增加是爆炸的,针对每一个新的操作系统的支持都需要增加新的抽象工厂和具体工程,类的个数成对增加,最后导致系统越来越庞大
- 在工厂方法模式中,具体工厂负责生产具体的产品,每个具体工厂对应一种具体产品,工厂方法具有唯一性,显然,实例所处的业务场景是具有多个产品对象的,并不是单一的产品对象
- 当系统所提供的工厂生产的具体产品并不是一个单一产品,而是多个位于不同产品等级结构、属于不同类型的具体产品时,就可以使用抽象工厂模式
- 抽象工厂模式示意图如下:
- 产品等级结构:产品的继承结构
- 产品族:指同一个工厂生产的位于不同产品等级结构中的一组产品
抽象工厂模式
概念
- 抽象工厂模式(
Abstract Factory Pattern
),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类 - 抽象工厂模式又称为
Kit
模式,它是一种对象创建型模式
抽象工厂模式解决方案
Operation.java
/**
* @Description 操作功能抽象类
*/
public abstract class Operation {
public abstract void init();
}
AndroidOperation.java
/**
* @Description Android操作功能产品类
*/
public class AndroidOperation extends Operation {
@Override
public void init() {
System.out.println("初始化Android操作功能");
}
}
IOSOperation.java
/**
* @Description IOS操作功能产品类
*/
public class IOSOperation extends Operation {
@Override
public void init() {
System.out.println("初始化IOS操作功能");
}
}
Surface.java
/**
* @Description 操作界面抽象类
*/
public abstract class Surface {
public abstract void init();
}
AndroidSurface.java
/**
* @Description Android操作界面产品类
*/
public class AndroidSurface extends Surface {
@Override
public void init() {
System.out.println("初始化Android操作界面");
}
}
IOSSurface.java
/**
* @Description IOS操作界面产品类
*/
public class IOSSurface extends Surface {
@Override
public void init() {
System.out.println("初始化IOS操作界面");
}
}
InitFactory.java
/**
* @Description 初始化工厂接口:抽象工厂
*/
public interface InitFactory {
Operation getOperation();
Surface getSurface();
}
AndroidInitFactory.java
/**
* @Description Android初始化工厂:具体工厂类
*/
public class AndroidInitFactory implements InitFactory{
@Override
public Operation getOperation() {
return new AndroidOperation();
}
@Override
public Surface getSurface() {
return new AndroidSurface();
}
}
IOSInitFactory.java
/**
* @Description IOS初始化工厂:具体工厂类
*/
public class IOSInitFactory implements InitFactory{
@Override
public Operation getOperation() {
return new IOSOperation();
}
@Override
public Surface getSurface() {
return new IOSSurface();
}
}
Test.java
/**
* @Description 抽象工厂测试类
* @author coisini
* @date Mar 4, 2022
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
InitFactory initFactory = new AndroidInitFactory();
Operation operation = initFactory.getOperation();
Surface surface = initFactory.getSurface();
operation.init();
surface.init();
initFactory = new IOSInitFactory();
operation = initFactory.getOperation();
surface = initFactory.getSurface();
operation.init();
surface.init();
}
}
- 输出如下:
初始化Android操作功能
初始化Android操作界面
初始化IOS操作功能
初始化IOS操作界面
- 类图如下:
- 抽象工厂可以将一组具有同一主题单独的工厂封装起来
- 抽象工厂模式与工厂方法模式最大的区别在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构
- 当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率
方案的演进(配置文件)
-
如上客户端
Test.java
调用代码还具有可改进的地方,可通过配置文件 + 反射实现在不修改客户端代码的基础上更换和扩展对操作系统的支持 -
config.properties
abstractFactory.className=AndroidInitFactory
PropertiesUtil.java
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @Description Properties工具类
* @author coisini
* @date Feb 28, 2022
* @version 1.0
*/
public class PropertiesUtil {
/**
* 根据key读取value
* @Description: 相对路径, properties文件需在classpath目录下,
* 比如:config.properties在包com.coisini.util,
* 路径就是/com/coisini/util/config.properties
* @param filePath
* @param keyWord
* @return String
* @throws
*/
private static String getProperties(String filePath, String keyWord){
Properties prop = new Properties();
String value = null;
try {
InputStream inputStream = PropertiesUtil.class.getResourceAsStream(filePath);
prop.load(inputStream);
value = prop.getProperty(keyWord);
} catch (IOException e) {
e.printStackTrace();
}
return value;
}
/**
* 根据配置文件提取类名返回实例对象
* @param filePath
* @param keyWord
* @param packagePath
* @return
*/
private static Object getBean(String filePath, String keyWord, String packagePath) {
try {
String className = getProperties(filePath, keyWord);
Class<?> c = Class.forName(packagePath + className);
return c.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取抽象工厂实例对象
* @return
*/
public static Object getAbstractFactoryBean() {
return getBean("/com/coisini/design/util/config.properties",
"abstractFactory.className",
"com.coisini.design.pattern.creational.abstractfactory.v3.");
}
}
Test.java
/**
* @Description 抽象工厂测试类
*/
public class Test {
public static void main(String[] args) {
InitFactory initFactory = (InitFactory) PropertiesUtil.getAbstractFactoryBean();
Operation operation = initFactory.getOperation();
Surface surface = initFactory.getSurface();
operation.init();
surface.init();
}
}
- 通过如上配置文件 + 反射实现方式,客户端代码无需使用
new
关键字来创建工厂对象,而是将具体工厂类的类名存在配置文件中,通过读取配置文件获取类名字符串,再使用反射机制根据类名字符串生成对象
总结
- 优点
1、具体产品在应用层代码隔离,无需关心创建细节
2、将一个系列的产品族统一到一起创建
- 缺点
1、规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
2、增加了系统的抽象性和理解难度
- 适用场景
1、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
2、强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码
3、提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现