其他分享
首页 > 其他分享> > 04-工厂方法模式

04-工厂方法模式

作者:互联网

文章目录


代码:https://github.com/nateshao/design-demo/tree/main/JavaDesignPatterns/factorymethod

1. 工厂方法模式概述

工厂方法模式:

工厂方法模式的定义

工厂方法模式:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类

2. 工厂方法模式的结构

工厂方法模式包含以下4个角色:

  1. Product(抽象产品)
  2. ConcreteProduct(具体产品)
  3. Factory(抽象工厂)
  4. ConcreteFactory(具体工厂)

工厂方法模式的实现

典型的抽象工厂类代码:

public interface Factory {
    public Product factoryMethod();
}

典型的具体工厂类代码:

public class ConcreteFactory implements Factory {
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
}

典型的客户端代码片段:

……
Factory factory;
factory = new ConcreteFactory(); //可通过配置文件和反射机制实现
Product product;
product = factory.factoryMethod();
……

3. 工厂方法模式的应用实例

某系统运行日志记录器(Logger)可以通过多种途径保存系统的运行日志,例如通过文件记录或数据库记录,用户可以通过修改配置文件灵活地更换日志记录方式。在设计各类日志记录器时,开发人员发现需要对日志记录器进行一些初始化工作,初始化参数的设置过程较为复杂,而且某些参数的设置有严格的先后次序,否则可能会发生记录失败。

为了更好地封装记录器的初始化过程并保证多种记录器切换的灵活性,现使用工厂方法模式设计该系统。

  1. Logger:日志记录器接口,充当抽象产品角色
  2. DatabaseLogger:数据库日志记录器,充当具体产品角色
  3. FileLogger:文件日志记录器,充当具体产品角色
  4. LoggerFactory:日志记录器工厂接口,充当抽象工厂角色
  5. DatabaseLoggerFactory:数据库日志记录器工厂类,充当具体工厂角色
  6. FileLoggerFactory:文件日志记录器工厂类,充当具体工厂角色
  7. Client:客户端测试类

结果及分析:在未使用配置文件和反射机制之前,更换具体工厂类需修改客户端源代码,但无须修改类库代码

4. 反射机制与配置文件

Java反射机制(Java Reflection)

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

配置文件

<!— config.xml -->
<?xml version="1.0"?>
<config>
    <className>designpatterns.factorymethod.FileLoggerFactory</className>
</config>

XMLUtil.java

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;
        }
    }
}

Client.java

public class Client {
    public static void main(String args[]) {
        LoggerFactory factory;
        Logger logger;
        factory = (LoggerFactory)XMLUtil.getBean(); //getBean()的返回类型为Object,需要进行强制类型转换
        logger = factory.createLogger();
        logger.writeLog();
    }
}

增加新产品的步骤

  1. 增加一个新的具体产品类作为抽象产品类的子类
  2. 增加一个新的具体工厂类作为抽象工厂类的子类,该工厂用于创建新增的具体产品对象
  3. 修改配置文件,用新的具体工厂类的类名字符串替换原有工厂类类名字符串
  4. 编译新增具体产品类和具体工厂类,运行客户端代码,即可完成新产品的增加和使用

5.工厂方法的重载

结构图

抽象工厂类LoggerFactory示意代码

public interface LoggerFactory {
    public Logger createLogger();
    public Logger createLogger(String args);
    public Logger createLogger(Object obj);
}

具体工厂类DatabaseLoggerFactory示意代码:

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;
	}	
}
//其他具体工厂类代码省略

6. 工厂方法的隐藏

目的:为了进一步简化客户端的使用

实现:在工厂类中直接调用产品类的业务方法,客户端无须调用工厂方法创建产品对象,直接使用工厂对象即可调用所创建的产品对象中的业务方法

抽象工厂类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(); //直接使用工厂对象来调用产品对象的业务方法
    }
}

7. 工厂方法模式的优缺点与适用环境

模式优点

  1. 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
  2. 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
  3. 在系统中加入新产品时,完全符合开闭原则

模式缺点

  1. 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
  2. 增加了系统的抽象性和理解难度

模式适用环境

  1. 客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)
  2. 抽象工厂类通过其子类来指定创建哪个对象

主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
3. 在系统中加入新产品时,完全符合开闭原则

模式缺点

  1. 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
  2. 增加了系统的抽象性和理解难度

模式适用环境

  1. 客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)
  2. 抽象工厂类通过其子类来指定创建哪个对象

标签:记录器,04,模式,工厂,具体,Logger,public
来源: https://blog.csdn.net/weixin_47222709/article/details/120855669