设计模式(六):简单工厂模式、工厂方法模式、抽象工厂模式
创建型模式 之 简单工厂模式、工厂方法模式、抽象工厂模式。
工厂模式是比较常见,应用较为广泛的模式,工厂模式主要用于创建对象,把对象的创建和使用进行分离。
本篇对这三种工厂模式进行描述和对比分析。
简单工厂模式
模式定义
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。
在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
模式分析
简单工厂模式 包含三个角色,工厂角色负责实现创建所有实例的内部逻辑;抽象产品 角色是所有创建的对象的父类;具体产品 角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
- 将对像的创建与使用分离,降低了系统的耦合度,两者修改起业相对容易。
- 工厂类的方法是静态的,可使用类名直接调用,只需传入参数即可,使用起来方便。
- 要点:当需要什么,只需传入一个正确的参数,就可获取所需的对象,无须知道其创建细节。
- 缺点:工厂类的职责过重,增加新的产品需要修改工厂类的判断逻辑,违背了【开闭原则】,当产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
模式应用
简单工厂模式:如果要创建的产品不多,只要一个工厂类就可完成。它不属于 GoF 的 23 种经典设计模式,其缺点是增加新产品时违背了 【开闭原则】。
典型应用
JDK类库中广泛使用了简单工厂模式,如工具类 java.text.DateFormat,它用于格式化一个本地日期或者时间。
1
2
3
4
5public final static DateFormat getTimeInstance();
public final static DateFormat getDateInstance();
public final static DateFormat getDateTimeInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style, Locale locale);Mybatis 的 SqlSessionManager 是个典型的简单工厂模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
public static SqlSessionManager newInstance(Reader reader) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));
}
public static SqlSessionManager newInstance(Reader reader, String environment) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null));
}
public static SqlSessionManager newInstance(Reader reader, Properties properties) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, properties));
}
public static SqlSessionManager newInstance(InputStream inputStream) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, null));
}
public static SqlSessionManager newInstance(InputStream inputStream, String environment) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, environment, null));
}
public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties));
}
public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionManager(sqlSessionFactory);
}
//..........................
}Java 加密技术
获取不同加密算法的密钥生成器:
1
KeyGenerator keyGen = KeyGenerator.getInstance("DESede");
创建密码器:
1
Cipher cp = Cipher.getInstance("DESede");
代码示例
工厂角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class CarFactory {
public static Car createCar(String type) {
Car car = null;
switch (type) {
case "top":
car = new TopLevelCar();
break;
case "senior":
car = new SeniorCar();
break;
}
return car;
}
}抽象产品角色
1
2
3
4public class Car {
private Integer wheel = 4;
private Integer engine = 1;
}具体产品角色
1
2
3
4
5
6
7
8
9
10
11
12
13/**
* 顶配
*/
public class TopLevelCar extends Car {
private Integer price = 300000;
}
/**
* 高配
*/
public class SeniorCar extends Car {
private Integer price = 280000;
}
工厂方法模式
模式定义
工厂方法模式(Factory Method Pattern):定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的 创建与使用分离 的特点。工厂方法模式又称为多态工厂。
工厂类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
模式分析
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。
这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色(已有代码)的情况下引进新产品,满足了 【开闭原则】。
工厂方法模式 由抽象工厂,具体工厂,抽象产品和具体产品 4 个要素构成。
- 具体工厂创建具体的产品,用户无须关心创建细节,只需关于所需产品对应的工厂。
- 具体工厂可自主确定创建何种产品对象,创建细节完全封装在具体的工厂内部。所有具体工厂都具有同一抽象父类。
- 加入新产品时,无须修改已有的抽象工厂,具体工厂和具体产品,只需添加一个具体工厂和具体产品即可。增加了系统的扩展性,符合【开闭原则】。
- 缺点:当具体产品较多时,每增加一个产品,就要增加一个具体产品类和对应的具体工厂类,一定程序上增加了系统的复杂度。
模式应用
典型应用
JDBC 中的工厂方法:
1 | Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db_sys_user?characterEncoding=utf-8", "admin", "123456"); |
创建连接实际是在内部判断是否存在具体的 JDBC 驱动,由 具体的 JDBC 的驱动完成的,而不在驱动管理器中。
1 | Connection con = aDriver.driver.connect(url, info); |
代码示例
抽象工厂
1
2
3
4
5
6
7
8
9
10
11/**
* 产品工厂公共抽象
*/
public abstract class AbstractCarFactory {
/**
* 公共接口
* @return
*/
public abstract Car createCar();
}具体工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* 产品具体工厂
*
* 生产高配汽车
*/
public class TopLevelCarFactory extends AbstractCarFactory {
public TopLevelCar createCar() {
return new TopLevelCar();
}
}
/**
* 产品具体工厂
*
* 生产中配汽车
*/
public class SeniorCarFactory extends AbstractCarFactory {
public SeniorCar createCar() {
return new SeniorCar();
}
}抽象产品
1
2
3
4
5
6/**
* 产品抽象
*/
public abstract class Car {
public abstract void show();
}具体产品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* 具体产品
*
* 顶配汽车
*/
public class TopLevelCar extends Car {
public void show() {
System.out.println("这是一辆 顶配 的汽车");
}
}
/**
* 具体产品
*
* 高配汽车
*/
public class SeniorCar extends Car {
public void show() {
System.out.println("这是一辆 高配 的汽车");
}
}调用具体工厂生产具体产品
1
2
3
4
5
6
7
8
9
10
11
12public class MainMethod {
public static void main(String[] args) {
//通过具体工厂创建具体产品
TopLevelCarFactory topLevelCarFactory = new TopLevelCarFactory();
topLevelCarFactory.createCar().show();
SeniorCarFactory seniorCarFactory = new SeniorCarFactory();
seniorCarFactory.createCar().show();
}
}
//这是一辆 顶配 的汽车
//这是一辆 高配 的汽车
抽象工厂模式
抽象工厂模式 是对 工厂方法模式 在某些场景下的升级。所以从整篇文章可以看到,简单工厂 到 工厂方法 到抽象工厂,是逐步升级的模式,越来越抽象,逐步更深层次的解耦,更优的扩展性,适合较为复杂的场景。
模式定义
抽象工厂模式(Abstract Factory Pattern)提供用于创建一组相关或依赖对象的接口,而无须指定具体的类,就能得到 同族不同等级 的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品(只生产一个等级的产品),工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但有时候需要一个工厂可以提供多个产品对象,而抽象工厂模式可以创建一组产品(多个等级)。
简单理解:工厂方法提供一个公共接口,实现此接口的具体产品工厂通常只能创建同一种产品;而抽象工厂提供一组生产产品的接口,实现这些接口的具体工厂可以创建一组产品,一组产品中的不同产品可归为一个产品族(同一个工厂生产),但是不同等级的产品(不同产品线)。
模式分析
优点
- 具体工厂都实现了抽象工厂中定义的公共接口,隔离了生产产品的具体类(不再是某一个具体产品工厂类),基于这种隔离,可以在具体工厂的内部对产品簇中的相关联的多等级的产品共同管理。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要 根据当前环境来决定其行为的软件系统 来说,是一种非常实用的设计模式。
- 增加新的具体工厂和新的产品族中时非常方便,无须修改已有系统,满足 开闭原则。
缺点
- 当产品族中增加一个新的产品时,所有的工厂类都需要进行修改,因为在抽象工厂角色中规定了所有可能被创建的产品集合,就会较为不方便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,不需要修改原代码,满足开闭原则;当增加新种类的产品时,所有的工厂类都需要修改,不满足开闭原则)。
模式退化
- 当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式。
- 当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。
模式应用
抽象工厂模式适以场景:如果系统中有多个产品族,需要创建同一族但属于不同等级结构的产品;或 系统一次只可能使用其中某一族产品,即同族的产品一起使用。
例如:在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时,可以使用抽象工厂模式进行设计。
典型应用
java.sql.Connection
1
2
3
4
5
6
7
8
9public interface Connection extends Wrapper, AutoCloseable {
//返回普通的 SQL 执行器
Statement createStatement() throws SQLException;
//返回预编译的 SQL 执行器
PreparedStatement prepareStatement(String sql) throws SQLException;
//返回可执行存储过程的 SQL 执行器
CallableStatement prepareCall(String sql) throws SQLException;
}Connection 抽象提供了创建 SQL 执行器的三个公共接口,这三个接口提供的是同一产品族,不同等级的产品。可以跟踪其源码来了解具体的工厂及生产的具体产品。
org.apache.ibatis.session.SqlSessionFactory
Mybatis 中的 SqlSessionFactory。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}SqlSessionFactory 也是抽象工厂接口,Configuration 和SqlSession 都是在不同的产品等级上。
代码示例
抽象工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* 产品工厂公共抽象
*/
public interface AbstractCarFactory {
/**
* 公共接口
*
* @return
*/
IBus createBus();
ITruck createTruck();
}具体工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* 比亚迪汽车工厂
*/
public class BYDCarFactory implements AbstractCarFactory {
public IBus createBus() {
return new BYDBus();
}
public ITruck createTruck() {
return new BYDTruck();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* 福特汽车工厂
*/
public class FuTeCarFactory implements AbstractCarFactory {
public IBus createBus() {
return new FUTEBus();
}
public ITruck createTruck() {
return new FUTETruck();
}
}抽象产品
1
2
3
4
5
6/**
* 产品抽象(Bus)
*/
public interface IBus extends Car {
void show();
}1
2
3
4
5
6/**
* 产品抽象(Truck)
*/
public interface ITruck extends Car {
void show();
}具体产品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//比亚迪-巴士
public class BYDBus implements IBus {
public void show() {
System.out.println("this is a byd bus");
}
}
//比亚迪-卡车
public class BYDTruck implements ITruck {
public void show() {
System.out.println("this is a byd truck");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//福特-巴士
public class FUTEBus implements IBus {
public void show() {
System.out.println("this is a fute bus");
}
}
//福特-卡车
public class FUTETruck implements ITruck {
public void show() {
System.out.println("this is a fute truck");
}
}通过具体工厂创建产品族中不同等级的产品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class MainMethod {
public static void main(String[] args) {
//不需要知道具体的产品工厂
AbstractCarFactory byd = new BYDCarFactory();
IBus bydBus = byd.createBus();
ITruck bydTruck = byd.createTruck();
bydBus.show();
bydTruck.show();
AbstractCarFactory fute = new FuTeCarFactory();
IBus futeBus = byd.createBus();
ITruck futeTruck = byd.createTruck();
futeBus.show();
futeTruck.show();
}
}
参考引用
设计模式(六):简单工厂模式、工厂方法模式、抽象工厂模式
http://blog.gxitsky.com/2019/09/14/DesignPatterns-06-Factory/