周怡

  • 首页
  • 文章归档
  • 默认分类
  • 关于页面

  • 搜索
Java Spring Boot Spring JVM 正则表达式 NodeJS CSS 微服务 MySQL Nginx 设计模式

简单工厂模式

发表于 2020-06-21 | 0 | 阅读次数 304

简单工厂概述

工厂模式是使用频率很高的一类创建型模式,通常所说的工厂模式是指工厂方法模式。简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都有共同的父类。使用简单工厂的时候,通常不用创建简单工厂类的类实例,可以把简单工厂类实现成一个工具类,直接使用静态方法,也就是说简单工厂用于创建实例的方法通常都是静态的,所以也被称为静态工厂模式,它是工厂方法模式的弱化,并不属于GoF 23种设计模式。

简单工厂结构

简单工厂模式的结构如下图所示:

simple_factory

在简单工厂模式结构图种包含以下几个角色:

  • Factory(工厂角色):它是简单工厂模式的核心,负责实现创建所有Api实例的逻辑,工厂类可以被外部(客户端)直接调用,创建所需Api对象。
  • Api(抽象Api角色):它是工厂类所创建的所有对象的接口,封装了用户api公有的方法。
  • ApiImpl(具体Api角色):它是简单工厂模式的创建目标。每一个具体的Api都实现或者继承了抽象Api,需要实现在Api接口中声明的抽象方法。

对图中出现的角色简短的描述就是:

Api:定义客户所需要的功能接口

Impl:具体实现Api接口的实现类,可能会有多个

Factory:工厂类,通过传入的参数选择合适的实现类来创建Api接口对象

Client:客户端,通过Factory去获取Api接口对象,面向Api接口编程

示例代码

Api接口定义,定义了公共方法:

/**
 * Api 接口定义
 */
public interface Api {

  /**
   * 具体的功能方法的定义,打印输出
   */
  public void print(String s);
}

Api接口实现类:

/**
 * Api接口具体实现类A
 */
public class ApiImplA implements Api {

  @Override
  public void print(String s) {
    System.out.println(s + " 正在打印...");
  }

}

/**
 * Api接口具体实现类B
 */
public class ApiImplB implements Api {

  @Override
  public void print(String s) {
    System.out.println(s + " 正在打印...");
  }

}

典型的简单工厂代码如下所示:

/**
 * 工厂类,用来生产Api的对象实例
 */
public class Factory {

  public static Api produceApi(String arg) {
    Api api = null;
    if (arg.equals("A")) {
      api = new ApiImplA();
    } else if (arg.equals("B")) {
      api = new ApiImplB();
    }
    return api;
  }
}

客户端调用示例代码:

/**
 * 客户端
 */
public class Client {
  
  public static void main(String[] args) {
    Api apiA = Factory.produceApi("A");
    apiA.print("使用简单工厂创建的A对象");
    
    Api apiB = Factory.produceApi("B");
    apiB.print("使用简单工厂创建的B对象");
  }
  
}

在java中,讲究面向接口编程,而接口的思想就是封装隔离,Api的接口实现类ImplA和ImplB,应该是被接口Api封装并同客户端隔离开的。所以客户端可以通过工厂类来创建具体的Api对象,而不再使用new关键字来创建对象。

注意到以上实现简单工厂模式有一个缺点:从客户端在调用工厂创建对象的时候,需要传入选择的参数,这就说明客户端必须知道每个参数的含义,也需要理解每个参数对应的功能处理。这就要求必须在一定程度上,向客户暴露了一定的内部实现细节。

可配置的简单工厂

在现有的Api实现中,每更换或者新增一种Api的接口实现,都需要修改客户端代码中静态工厂方法的参数,那么有没有一种方法能够在不修改客户端代码的前提下更换Api的实现?

一种常用的实现方式是将静态工厂方法的参数存储在properties或xml格式的配置文件中。

把参数存储在如下factory.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<api>
  <type>simple_factory.ApiImplA</type>
</api>

用一个工具类ConfigUtil读取factory.xml中配置的参数:

import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ConfigUtil {

  public static String getApiType() {

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    try {
      DocumentBuilder db = dbf.newDocumentBuilder();
      Document doc = db.parse(new File("src/simple_factory/factory.xml"));
      NodeList nl = doc.getElementsByTagName("type");
      Node node = nl.item(0).getFirstChild();
      return node.getNodeValue().trim();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
}

引入ConfigUtil工具类后修改工厂类代码:

/**
 * 工厂类,用来生产Api的对象实例
 */
public class Factory {

  public static Api produceApi() {
    Api api = null;
    String arg = ConfigUtil.getApiType();
    /**
     * 通过反射创建对象
     */
    try {
      api = (Api)Class.forName(arg).newInstance();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }  
    return api;
  }
}

此时,客户端无需再传入参数:

/**
 * 客户端
 */
public class Client {
  
  public static void main(String[] args) {
    Api apiA = Factory.produceApi();
    apiA.print("使用简单工厂创建的对象");
  }
}

如果需要更换具体的Api对象,只需修改factory.xml文件,不用修改工厂类以及客户端代码,符合“开闭原则”。

简单工厂模式本质

简单工厂模式提供工厂类选择具体的实现来创建对象,将对象的创建跟对象的使用分开,从而使得客户端和具体实现之间解耦,因此要是具体实现发生变化,就不再需要相应地去修改客户端了。

适用场景

  • 需要完全封装隔离的具体实现,客户端只能通过接口来操作封装的内部逻辑,知道传入的参数,但并不关心具体对象的创建。
  • 需要创建的对象较少,想把外部创建对象的职责集中管理和控制,这样工厂方法内部逻辑也不至于太过复杂。
  • 本文作者: 周怡
  • 本文链接: https://www.zhouyi.tech/archives/简单工厂模式
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# Java # Spring Boot # Spring # JVM # 正则表达式 # NodeJS # CSS # 微服务 # MySQL # Nginx # 设计模式
工厂模式
工厂方法模式
  • 文章目录
  • 站点概览
周怡

周怡

30 日志
5 分类
11 标签
RSS
Creative Commons
© 2021 周怡
由 Halo 强力驱动
|
主题 - NexT.Mist v5.1.4