2-工厂方法模式

工厂方法模式

参考:

工厂方法模式的定义

image-20221121190626440

通常而言的使用了工厂指的就是工厂方法模式

  • 简称为工厂模式(Factory Pattern)

  • 又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)

  • 工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象

  • 目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类

工厂方法模式结构

image-20221121191000614
工厂方法模式包含以下角色
  • Product(抽象产品)

  • ConcreteProduct(具体产品)

  • Factory(抽象工厂)

  • ConcreteFactory(具体工厂)

典型的抽象工厂类代码
1
2
3
public interface Factory {
public Product factoryMethod();
}
典型的具体工厂类代码
1
2
3
4
5
public class ConcreteFactory implements Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
典型的客户端代码片段
1
2
3
4
5
6
……
Factory factory;
factory = new ConcreteFactory(); //可通过配置文件和反射机制实现
Product product;
product = factory.factoryMethod();
……
实例类图1:
image-20221121191924490 image-20221121192203153

具体关键代码见书P46

(1) Logger:日志记录器接口,充当抽象产品角色

(2) DatabaseLogger:数据库日志记录器,充当具体产品角色

(3) FileLogger:文件日志记录器,充当具体产品角色

(4) LoggerFactory:日志记录器工厂接口,充当抽象工厂角色

(5) DatabaseLoggerFactory:数据库日志记录器工厂类,充当具体工厂角色

(6) FileLoggerFactory:文件日志记录器工厂类,充当具体工厂角色

(7) Client:客户端测试类

Java反射机制(Java Reflection)

  • Java反射(Java Reflection)是指在程序运行时获取已知名称的类或已有对象的相关信息的一种机制,包括类的方法、属性、父类等信息,还包括实例的创建和实例类型的判断等

  • Class类的实例表示正在运行的Java应用程序中的类和接口,其forName(String className)方法可以返回与带有给定字符串名的类或接口相关联的 Class对象,再通过Class对象的newInstance()方法创建此对象所表示的类的一个新实例,即通过一个类名字符串得到类的实例

1
2
3
4
//通过类名生成实例对象并将其返回
Class c=Class.forName("java.lang.String");
Object obj=c.newInstance();
return obj;

java父类转换成子类需要使用(Type)obj进行强制转换。

java类型强制转换只有自己转成自己的类型转换成父类的类型是安全的。只有这两种转换方式不会报错。

  • 原本是==Object类型(最大父类型)==的实例可以转换成任何类型。

配置文件

配置文件

//存储类名:包名.类名

1
2
3
4
5
6
<!— config.xml -->
<?xml version="1.0"?>
<config>
<className>designpatterns.factorymethod.FileLoggerFactory</className>
</config>

XMLUtil.java文件
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
package designpatterns.factorymethod;

//XMLUtil.java
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;

public class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean() {
try {
//创建DOM文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src//designpatterns//factorymethod//config.xml"));

//获取包含类名的文本结点
NodeList nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();

//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}

客户端代码
1
2
3
4
5
6
7
8
9
10
11
package designpatterns.factorymethod;

public class Client {
public static void main(String args[]) {
LoggerFactory factory; //将LoggerFactory作为参数类型(只有作类属性的时候才是关联),所以是依赖关系
Logger logger; //将Logger作为参数类型,所以是依赖关系
factory = (LoggerFactory)XMLUtil.getBean(); //getBean()的返回类型为Object,需要进行强制类型转换
logger = factory.createLogger();
logger.writeLog();
}
}

通过简单的反射创建工厂,再由工厂创建复杂的对象(产品)

配置文件是纯文本文件,不需要编译。

增加新产品的步骤

(1) 增加一个新的具体产品类作为抽象产品类的子类

(2) 增加一个新的具体工厂类作为抽象工厂类的子类,该工厂用于创建新增的具体产品对象

(3) 修改配置文件,用新的具体工厂类的类名字符串替换原有工厂类类名字符串

(4) 编译新增具体产品类和具体工厂类,运行客户端代码,即可完成新产品的增加和使用

以上完全符合开闭原则

工厂方法的重载

结构图
image-20221121195730772
抽象工厂类LoggerFactory示意代码
1
2
3
4
5
public interface LoggerFactory {
public Logger createLogger();
public Logger createLogger(String args);
public Logger createLogger(Object obj);
}
具体工厂类DatabaseLoggerFactory示意代码
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 DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//使用默认方式连接数据库,代码省略
Logger logger = new DatabaseLogger();
//初始化数据库日志记录器,代码省略
return logger;
}

public Logger createLogger(String args) {
//使用参数args作为连接字符串来连接数据库,代码省略
Logger logger = new DatabaseLogger();
//初始化数据库日志记录器,代码省略
return logger;
}

public Logger createLogger(Object obj) {
//使用封装在参数obj中的连接字符串来连接数据库,代码省略
Logger logger = new DatabaseLogger();
//使用封装在参数obj中的数据来初始化数据库日志记录器,代码省略
return logger;
}
}
//其他具体工厂类代码省略

工厂方法的隐藏

违反了单一职责原则

  1. 目的:为了进一步简化客户端的使用
  2. 实现:在工厂类中直接调用产品类的业务方法,客户端无须调用工厂方法创建产品对象,直接使用工厂对象即可调用所创建的产品对象中的业务方法
结构图
image-20221121200102222
抽象工厂类LoggerFactory示意代码
1
2
3
4
5
6
7
8
9
10
//将接口改为抽象类
public abstract class LoggerFactory {
//在工厂类中直接调用日志记录器类的业务方法writeLog()
public void writeLog() {
Logger logger = this.createLogger();
logger.writeLog();
}

public abstract Logger createLogger();
}
客户端代码
1
2
3
4
5
6
7
public class Client {
public static void main(String args[]) {
LoggerFactory factory;
factory = (LoggerFactory)XMLUtil.getBean();
factory.writeLog(); //直接使用工厂对象来调用产品对象的业务方法
}
}

模式优点

  • 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节

  • 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部

    • 符合单一职责原则
    • 每个工厂只负责一个产品的创建
  • 在系统中加入新产品时,完全符合开闭原则

模式缺点

  • 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销

  • 增加了系统的抽象性和理解难度

模式适用环境

  • 客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)

    • 每个工厂只负责一个产品的创建,换了工厂就换了产品
  • 抽象工厂类通过其子类来指定创建哪个对象

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2023-2024 Guijie Wang
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信