一、工厂设计模式介绍
在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。
复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况,因为类的构造过于复杂,如果直接在其他业务类内使用,则两者的耦合过重,后续业务更改,就需要在任何引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。
按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。
二、简单工厂模式
1.什么是简单工厂模式
我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern),本文给的案例没有将方法改为静态方法,读者可以自行尝试。
2.简单工厂模式的结构
简单工厂模式的主要角色如下:
- 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
- 抽象对象(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
-
具体对象(Television、Fan、Computer):是简单工厂模式的创建目标。
3.简单工厂模式的实现
public abstract class Product {
private String name;
/**
* 实现功能
*/
public abstract void doFunction();
public void setName(String name) {
this.name = name;
}
}
public class Computer extends Product {
@Override
public void doFunction() {
System.out.println("我可以用来打电脑游戏");
}
}
public class Fan extends Product {
@Override
public void doFunction() {
System.out.println("我可以用来吹风");
}
}
public class Television extends Product {
@Override
public void doFunction() {
System.out.println("我可以用来看电视");
}
}
public class SimpleFactory {
private static final String COMPUTER = "computer";
private static final String FAN = "fan";
private static final String TELEVISION = "television";
public Product createProduct(String type){
Product product = null;
if (FAN.equals(type)) {
product = new Fan();
product.setName(type);
} else if (TELEVISION.equals(type)) {
product = new Television();
product.setName(type);
} else if (COMPUTER.equals(type)) {
product = new Computer();
product.setName(type);
}
return product;
}
}
4.简单工厂模式优缺点与应用场景
优点:
1)工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
2)客户端无需知道所创建具体产品的类名,只需知道参数即可。
3)也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。缺点:
1)简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
2)使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
3)系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂应用场景
对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。
三、工厂方法模式
1.什么是工厂方法模式
前面我们介绍了简单工厂模式,提到了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
工厂方法模式(Factory Method Pattern)也被称为多态工厂模式,其定义了一个创建某种产品的接口,但由子类决定要实例化的产品是哪一个,从而把产品的实例化推迟到子类。
2.工厂方法设计模式的结构
工厂方法模式的主要角色如下:
- 抽象工厂(AbstractFactory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 crateProduct() 来创建产品。
- 具体工厂(FanFactory、ComputerFactory、TelevisionFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
-
具体产品(Televison、Fan、Computer):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
3.工厂方法模式的实现
public abstract class Product {
private String name;
/**
* 实现功能
*/
public abstract void doFunction();
public void setName(String name) {
this.name = name;
}
}
public class Computer extends Product {
@Override
public void doFunction() {
System.out.println("我可以用来打电脑游戏");
}
}
public class Fan extends Product {
@Override
public void doFunction() {
System.out.println("我可以用来吹风");
}
}
public class Television extends Product {
@Override
public void doFunction() {
System.out.println("我可以用来看电视");
}
}
public interface AbstractFactory {
Product createProduct();
}
public class ComputerFactory implements AbstractFactory{
@Override
public Product createProduct() {
return new Computer();
}
}
public class FanFactory implements AbstractFactory{
@Override
public Product createProduct() {
return new Fan();
}
}
public class TelevisionFactory implements AbstractFactory{
@Override
public Product createProduct() {
return new Television();
}
}
4.工厂方法模式优缺点与应用场景
优点:
1)用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的2具体创建过程。
2)灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
3)典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。缺点:
1)类的个数容易过多,增加复杂度
2)增加了系统的抽象性和理解难度
3)抽象产品只能生产一种产品,此弊端可使用[抽象工厂模式解决。应用场景:
1)客户只知道创建产品的工厂名,而不知道具体的产品名。
2)创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
3)客户不关心创建产品的细节,只关心产品的品牌
三、抽象工厂模式
1.什么是抽象工厂模式
本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,图1所示的是海尔工厂和 TCL 工厂所生产的电视机与空调对应的关系图。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
2.抽象工厂模式的结构
工厂方法模式的主要角色如下:
- 抽象工厂(AbstractFactory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂(TclFactory、GreeFactory):主要是实现抽象工厂中的创造产品的多个抽象方法,完成某个品牌的具体产品的创建。
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
-
具体产品:实现了抽象产品角色所定义的内容。
3.抽象工厂模式的实现
public interface IFan {
void blow();
}
public interface ITelevision {
void watch();
}
public class GreeFan implements IFan {
@Override
public void blow() {
System.out.println("吹格力电风扇");
}
}
public class TclFan implements IFan{
@Override
public void blow() {
System.out.println("吹Tcl电风扇");
}
}
public class GreeTelevision implements ITelevision{
@Override
public void watch() {
System.out.println("看格力电视机");
}
}
public class TclTelevision implements ITelevision {
@Override
public void watch() {
System.out.println("看TCL电视机");
}
}
public abstract class AbstractFactory {
public void init() {
System.out.println("都要执行的通用的工厂方法");
}
protected abstract IFan createFan();
protected abstract ITelevision createTelevision();
}
public class GreeFactory extends AbstractFactory{
@Override
protected IFan createFan() {
super.init();
return new GreeFan();
}
@Override
protected ITelevision createTelevision() {
super.init();
return new GreeTelevision();
}
}
public class TclFactory extends AbstractFactory{
@Override
protected IFan createFan() {
super.init();
return new TclFan();
}
@Override
protected ITelevision createTelevision() {
super.init();
return new TclTelevision();
}
}
4.抽象工厂模式优缺点与应用场景
优点
1)抽象工厂模式除了具有工厂方法模式的优点外,还可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
2)当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
3)抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。缺点
1)当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。应用场景
跟工厂方法模式应用场景类似,只是更加细化了。
五、工厂设计模式在实际中的应用
1.工厂设计模式在mybatis源码中的应用
源码详见:cn.bugstack.mybatis.session.SqlSessionFactory
public interface SqlSessionFactory {
SqlSession openSession();
}
源码详见:cn.bugstack.mybatis.session.defaults.DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
TransactionFactory transactionFactory = environment.getTransactionFactory();
tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
// 创建执行器
final Executor executor = configuration.newExecutor(tx);
// 创建DefaultSqlSession
return new DefaultSqlSession(configuration, executor);
} catch (Exception e) {
try {
assert tx != null;
tx.close();
} catch (SQLException ignore) {
}
throw new RuntimeException("Error opening session. Cause: " + e);
}
}
}
- 工厂模式:简单工厂,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例对象的类型。
-
场景介绍:
SqlSessionFactory
是获取会话的工厂,每次我们使用 Mybatis 操作数据库的时候,都会开启一个新的会话。在会话工厂的实现中负责获取数据源环境配置信息、构建事务工厂、创建操作SQL的执行器,并最终返回会话实现类。 -
同类设计:
SqlSessionFactory
、ObjectFactory
、MapperProxyFactory
、DataSourceFactory
2.工厂设计模式在slf4j源码中的应用
我们来看一下slf4j中是如何应用工厂方法模式的:
首先slf4j定义一个抽象工厂ILoggerFactory
public interface ILoggerFactory {
Logger getLogger(String var1);
}
接着定义了一个抽象产品Logger:
public interface Logger {
... ...
}
public class SubstituteLogger implements Logger {
private final String name;
private volatile Logger _delegate;
private Boolean delegateEventAware;
private Method logMethodCache;
private EventRecodingLogger eventRecodingLogger;
private Queue<SubstituteLoggingEvent> eventQueue;
private final boolean createdPostInitialization;
public SubstituteLogger(String name, Queue<SubstituteLoggingEvent> eventQueue, boolean createdPostInitialization) {
this.name = name;
this.eventQueue = eventQueue;
this.createdPostInitialization = createdPostInitialization;
}
)
具体工厂有很多,都实现了抽象工厂,这里展示其中一个SubstituteLoggerFactory
public class SubstituteLoggerFactory implements ILoggerFactory {
boolean postInitialization = false;
final Map<String, SubstituteLogger> loggers = new HashMap<String, SubstituteLogger>();
final LinkedBlockingQueue<SubstituteLoggingEvent> eventQueue = new LinkedBlockingQueue<SubstituteLoggingEvent>();
synchronized public Logger getLogger(String name) {
SubstituteLogger logger = loggers.get(name);
if (logger == null) {
logger = new SubstituteLogger(name, eventQueue, postInitialization);
loggers.put(name, logger);
}
return logger;
}
}
具体工厂用于生产具体的产品,SubstituteLogger就是一种具体的产品,从上边的代码中就可以看到,SubstituteLoggerFactory工厂在生产SubstituteLogger具体产品时,做了一定的处理。而真正使用时,只需要通过getLogger方法就可以得到具体产品,当然除了工厂设计模式这里还用到了单例设计模式,这就是设计模式的魅力。