设计模式-工厂方法模式

工厂方法模式

其它创建型模式链接:

  1. 设计模式-简单工厂模式
  2. 设计模式-抽象工厂模式
  3. 设计模式-建造者模式
  4. 设计模式-单例模式
  5. 设计模式-原型模式

概述

工厂方法模式定义

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

工厂方法模式又称作虚拟构造器或者多态工厂模式,是一种类创建型模式。在该模式下,父类负责定义创建产品对象的公共接口,而工厂子类负责生成具体的产品对象,这样做的目的是将实例化操作延迟到工厂子类,来弥补简单工厂的不足。

结构

image-20210512231735658

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

Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类。

ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品有专门的具体工厂创建,具体工厂与具体产品一一对应。

Factory(抽象工厂):在抽象工厂类中声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。

ConcreteFactory(具体工厂):它是抽象工厂的子类,实现了在抽象工厂中声明的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

应用实例

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

此时为了更好的封装记录器的初始化过程,并保证记录器切换的灵活性,使用工厂方法模式设计。

结构图

image-20210513081743021

实现

抽象产品

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

工厂方法模式优缺点以及适用环境

优点

工厂方法模式优点如下:

  1. 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同事向客户隐藏了哪种具体产品将会被实例化这一细节,用户只需要关心所需产品对应的工厂,无需关心产品名,甚至产品的类名
  2. 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键.它能够让工厂自主决定创建何种产品对象,而如何创建的细节封装在具体工厂内部.
  3. 在系统加入新的产品时无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他具体工厂和具体产品,只需要添加一个具体工厂和具体产品,系统的拓展性变得非常好,完全符合开闭原则

缺点

工厂方法模式的缺点如下:

  1. 在添加新产品时需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将会成对增加,在一定程度增加了系统的复杂度,有更多的类需要编译,会给系统增加额外开销
  2. 由于使用了抽象层,在客户端代码需要使用抽象层来定义产品,增加了系统的抽象性和理解难度

适用环境

以下情况下可以考虑使用工厂方法模式

  1. 客户端不知道它所需要的对象的类.在工厂方法模式中,客户端 不需要知道具体的产品类的类名,只需要知道对应工厂即可.具体产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或者数据库中
    给系统增加额外开销
  2. 由于使用了抽象层,在客户端代码需要使用抽象层来定义产品,增加了系统的抽象性和理解难度

适用环境

以下情况下可以考虑使用工厂方法模式

  1. 客户端不知道它所需要的对象的类.在工厂方法模式中,客户端 不需要知道具体的产品类的类名,只需要知道对应工厂即可.具体产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或者数据库中
  2. 抽象工厂类通过其子类来指定创建哪个对象.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值