工厂方法模式
其它创建型模式链接:
概述
工厂方法模式定义
定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
工厂方法模式又称作虚拟构造器或者多态工厂模式,是一种类创建型模式。在该模式下,父类负责定义创建产品对象的公共接口,而工厂子类负责生成具体的产品对象,这样做的目的是将实例化操作延迟到工厂子类,来弥补简单工厂的不足。
结构
工厂方法模式包含个4个角色
Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类。
ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品有专门的具体工厂创建,具体工厂与具体产品一一对应。
Factory(抽象工厂):在抽象工厂类中声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
ConcreteFactory(具体工厂):它是抽象工厂的子类,实现了在抽象工厂中声明的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
应用实例
某系统运行日志记录器,可以通过多种途径保存日志,例如通过文件记录或者数据库记录,用户可以通过修改配置文件来灵活的更换日志记录方式。在设计日志记录器时,开发人员需要对日志记录器进行一些初始化工作,初始化参数的设置过程较为复杂,而且某些参数的设置有严格的先后顺序,否则会发生记录失败。
此时为了更好的封装记录器的初始化过程,并保证记录器切换的灵活性,使用工厂方法模式设计。
结构图
实现
抽象产品
public interface Logger {
public void writeLog();
}
具体产品
public class DatabaseLogger implements Logger {
public void writeLog() {
System.out.println("数据库日志记录");
}
}
public class FileLogger implements Logger {
public void writeLog() {
System.out.println("文件日志记录");
}
}
抽象工厂
public interface LoggerFactory {
public Logger createLogger();
}
具体工厂
public class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//创建数据库连接
//创建数据库日志记录对象
Logger logger = new DatabaseLogger();
//初始化数据库日志记录器,代码省略
return logger;
}
}
public class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//创建文件日志记录器对象
Logger logger = new FileLogger();
//创建文件,代码省略
return logger;
}
}
客户端
public class Client {
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
factory = new FileLoggerFactory();
logger = factory.createLogger();
logger.writeLog();
}
}
结果展示
文件日志记录
通过映射机制使其符合开闭原则
通过映射机制来使客户端在不修改代码的情况下更换日志记录方式
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<config>
<className>com.factoryMethod.FileLoggerFactory</className>
</config>
读取配置文件的工具类
public class XMLUtil {
public static Object getBean(){
try {
//创建Document对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document document = builder.parse(new File("scr//resources//config.xml"));
//获取节点
NodeList nodeList = document.getElementsByTagName("className");
Node node = nodeList.item(0).getFirstChild();
String nodeValue = node.getNodeValue();
//通过类名生成实例
Class c = Class.forName(nodeValue);
Object obj = c.newInstance();
return obj;
}catch (Exception exception){
exception.printStackTrace();
return null;
}
}
}
客户端代码
public class Client {
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
factory = (LoggerFactory) XMLUtil.getBean();
logger = factory.createLogger();
logger.writeLog();
}
}
工厂方法模式优缺点以及适用环境
优点
工厂方法模式优点如下:
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同事向客户隐藏了哪种具体产品将会被实例化这一细节,用户只需要关心所需产品对应的工厂,无需关心产品名,甚至产品的类名
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键.它能够让工厂自主决定创建何种产品对象,而如何创建的细节封装在具体工厂内部.
- 在系统加入新的产品时无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他具体工厂和具体产品,只需要添加一个具体工厂和具体产品,系统的拓展性变得非常好,完全符合开闭原则
缺点
工厂方法模式的缺点如下:
- 在添加新产品时需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将会成对增加,在一定程度增加了系统的复杂度,有更多的类需要编译,会给系统增加额外开销
- 由于使用了抽象层,在客户端代码需要使用抽象层来定义产品,增加了系统的抽象性和理解难度
适用环境
以下情况下可以考虑使用工厂方法模式
- 客户端不知道它所需要的对象的类.在工厂方法模式中,客户端 不需要知道具体的产品类的类名,只需要知道对应工厂即可.具体产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或者数据库中
给系统增加额外开销 - 由于使用了抽象层,在客户端代码需要使用抽象层来定义产品,增加了系统的抽象性和理解难度
适用环境
以下情况下可以考虑使用工厂方法模式
- 客户端不知道它所需要的对象的类.在工厂方法模式中,客户端 不需要知道具体的产品类的类名,只需要知道对应工厂即可.具体产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或者数据库中
- 抽象工厂类通过其子类来指定创建哪个对象.