目录
1.0 ●工厂方法模式概述
✓不再提供一个按钮工厂类来统一负责所有产品的创建,而是将具体按钮的创建过程交给专门的工厂子类去完成。
✓如果出现新的按钮类型,只需要为这种新类型的按钮定义一个具体的工厂类就可以创建该新按钮的实例。
1.1 ●工厂方法模式的定义
工厂方法模式:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
✓简称为工厂模式(Factory Pattern)。
✓又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。
✓工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象。
✓目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
1.2 ●工厂方法模式的结构
工厂方法模式包含以下4个角色:
•Product(抽象产品)
•ConcreteProduct(具体产品)
•Factory(抽象工厂)
•ConcreteFactory(具体工厂)
1.3 ●实例说明
某系统运行日志记录器(Logger)可以通过多种途径保存系统的运行日志,例如通过文件记录或数据库记录,用户可以通过修改配置文件灵活地更换日志记录方式。在设计各类日志记录器时,开发人员发现需要对日志记录器进行一些初始化工作,初始化参数的设置过程较为复杂,而且某些参数的设置有严格的先后次序,否则可能会发生记录失败。
为了更好地封装记录器的初始化过程并保证多种记录器切换的灵活性,现使用工厂方法模式设计该系统。
1.3.1 ●实例代码分析
✓(1) Logger:日志记录器接口,充当抽象产品角色
✓(2) DatabaseLogger:数据库日志记录器,充当具体产品角色
✓(3) FileLogger:文件日志记录器,充当具体产品角色
✓(4) LoggerFactory:日志记录器工厂接口,充当抽象工厂角色
✓(5) DatabaseLoggerFactory:数据库日志记录器工厂类,充当具体工厂角色
✓(6) FileLoggerFactory:文件日志记录器工厂类,充当具体工厂角色
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description:日志记录器接口 充当抽象产品角色
* @Date: Create in 2018/10/9 9:39 PM
*/
public interface Logger {
public void writeLog();
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 文件日志记录器 充当具体的产品角色
* @Date: Create in 2018/10/9 9:44 PM
*/
public class FileLogger implements Logger {
@Override
public void writeLog() {
System.out.println("文件日志记录器");
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 数据库日志记录器 充当具体的产品角色
* @Date: Create in 2018/10/9 9:44 PM
*/
public class DataBaseLogger implements Logger{
@Override
public void writeLog() {
System.out.println("数据库日志记录器");
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 日志记录器工厂接口 充当抽象工厂角色
* @Date: Create in 2018/10/9 9:46 PM
*/
public interface LoggerFactory {
public Logger createLogger();
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 文件日志记录器工厂类
* @Date: Create in 2018/10/9 9:48 PM
*/
public class FileLoggerFactory implements LoggerFactory{
@Override
public Logger createLogger() {
Logger logger=new FileLogger();
return logger;
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 数据库日志记录器工厂类 充当具体工厂角色
* @Date: Create in 2018/10/9 9:52 PM
*/
public class DataBaseLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
Logger logger = new DataBaseLogger();
return logger;
}
}
<?xml version="1.0"?>
<config>
<!--<className>designpatterns.factoryMethod.demo.FileLoggerFactory2</className>-->
<className>designpatterns.factoryMethod.FileLoggerFactory</className>
</config>
//designpatterns.factorymethod.XMLUtil.java
package designpatterns.factoryMethod;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
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;
}
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description:
* @Date: Create in 2018/10/9 9:56 PM
*/
public class Client {
public static void main(String[] args) {
LoggerFactory loggerFactory;
Logger logger;
loggerFactory= (LoggerFactory) XMLUtil.getBean();
logger=loggerFactory.createLogger();
logger.writeLog();
}
}
编译运行结果如下;
如果需要更换日志记录器,只需要修改客户端代码中的具体工厂类的类名即可,例如将FileLoggerFactory改为DataBaseLoggerFactory,此时输出结果如下;
如果需要增加并使用新的日志记录器,只需要对应增加一个新的具体工厂类,然后修改客户端,原有类库的源代码无需做任何修改。
1.3.2 ●结果及分析
在未使用配置文件和反射机制之前,更换具体工厂类需修改客户端源代码,但无须修改类库代码。
1.4 ●工厂方法的重载
在某些情况下,可以通过多种方式来初始化同一个产品类,例如上面提到的日志记录器,可以为各种日志记录器提供默认实现;还可以为数据库日志记录器提供数据库链接字符串,为日志记录器提供文件路径。
抽象工厂类LoggerFactory示意代码:
public interface LoggerFactory {
public Logger createLogger();
public Logger createLogger(String args);
public Logger createLogger(Object obj);
}
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.5 ●工厂方法的隐藏
✓目的:为了进一步简化客户端的使用。
✓实现:在工厂类中直接调用产品类的业务方法,客户端无须调用工厂方法创建产品对象,直接使用工厂对象即可调用所创建的产品对象中的业务方法。
抽象工厂类LoggerFactory示意代码:
//将接口改为抽象类
public abstract class LoggerFactory {
//在工厂类中直接调用日志记录器类的业务方法writeLog()
public void writeLog() {
Logger logger = this.createLogger();
logger.writeLog();
}
public abstract Logger createLogger();
}
客户端代码:
public class Client {
public static void main(String args[]) {
LoggerFactory factory;
factory = (LoggerFactory)XMLUtil.getBean();
factory.writeLog(); //直接使用工厂对象来调用产品对象的业务方法
}
}
1.6 ●模式优点
✓工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节。
✓能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。
✓在系统中加入新产品时,完全符合开闭原则。
1.7 ●模式缺点
✓系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销。
✓增加了系统的抽象性和理解难度。
1.8 ●模式适用环境
✓客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)。
✓抽象工厂类通过其子类来指定创建哪个对象。
相关代码;
https://gitee.com/nanjunyu/designPatterns-study.git
原创公众号
关注java 设计模式,JVM特性,
并发编程、分布式、微服务,
inux高可用集群,等相关技术。
扫一扫关注我吧!