JAVA 设计模式系列——工厂模式

软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。

也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。

其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。

那么我们今天来看看设计模式中的第二种工厂模式中的工厂方法模式

在现实生活中社会分工越来越细,越来越专业化,各种产品有专门的工厂生产,彻底告别了自给自足的小农经济时代,这大大缩短了产品的生产周期,提高了生产效率。那么我们可能会考虑在软件开发中能否做到软件对象的生产和使用相分离呢?而这便需要我们的设计模式中的工厂模式

首先我们来回想一下,社会由最开始的自给自足原始社会,到农耕小作坊时代,到工业革命流水线,再到现代产业链代工厂,我们会发现生产的效率越来越高,其实整个社会的进步就是工厂模式发展的缩影。最开始自给自足是没有工厂;到小作坊简单完成商品的生产,为简单工厂模式;到工业革命流水线,商品完成自产自销,为工厂方法模式;到现阶段产业链代工厂,如现在的富士康等企业,为其他企业提供工厂流水线,为抽象工厂模式

什么是工厂模式呢?

定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。所有我们在实际开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。而按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。

所以紧接着我们来依次根据不同的场景看看这几种工厂模式。

简单工厂模式

我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫**“简单工厂模式”**。

在简单工厂模式中创建实例的方法通常为静态(static)方法

因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。

首先我们来看看简单工厂模式的组成:

  • 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
  • 具体产品(ConcreteProduct):是简单工厂模式的创建目标。

绘制结构图如下:

​ 图-1 简单工厂模式结构图

如果我们用 java 代码表示是怎样的呢?

/** * @author hz * @version 1.0 */public class Client {    //抽象产品    public interface Product {        void show();    }    //具体产品:ProductA    static class ConcreteProduct1 implements Product {        public void show() {            System.out.println("具体产品1显示...");        }    }    //具体产品:ProductB    static class ConcreteProduct2 implements Product {        public void show() {            System.out.println("具体产品2显示...");        }    }    final class Const {        static final int PRODUCT_A = 0;        static final int PRODUCT_B = 1;        static final int PRODUCT_C = 2;    }    static class SimpleFactory {        public static Product makeProduct(int kind) {            switch (kind) {                case Const.PRODUCT_A:                    return new ConcreteProduct1();                case Const.PRODUCT_B:                    return new ConcreteProduct2();            }            return null;        }    }}

复制代码

该模式的优缺点有哪些呢?

优点:

  1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  2. 客户端无需知道所创建具体产品的类名,只需知道参数即可。
  3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

缺点:

  1. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
  2. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
  3. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
  4. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

但是简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了**“开闭原则”**,因此我们进行进一步对简单工厂进行抽象,这个也是我们后面马上将给大家说的工厂模式的第二个实现场景,工厂方法模式。

工厂方法模式

前面我们说了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

工厂方法模式的主要角色包含:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

绘制结构图如下:

对比简单工厂模式,工厂方法模式在之间简单的工厂的基础之上将工厂进行拆分成抽象工厂和具体工厂两部分内容,这样灵活性得到大量提升。

而这种模式我们可以在那些场合使用呢?

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

如果我们用 java 代码表示是怎样的呢?

/** * @author hz * @version 1.0 */public class AbstractFactoryTest {    public static void main(String[] args) {        try {            Product a;            AbstractFactory af;            af = (AbstractFactory) ReadXML.getObject();            //抽象工厂内容放入到外部配置文件xml/properties等文件中,通过I/O流加载从而创建出抽象工厂            a = af.newProduct();            a.show();        } catch (Exception e) {            System.out.println(e.getMessage());        }    }}//抽象产品:提供了产品的接口interface Product {    public void show();}//具体产品1:实现抽象产品中的抽象方法class ConcreteProduct1 implements Product {    public void show() {        System.out.println("具体产品1显示...");    }}//具体产品2:实现抽象产品中的抽象方法class ConcreteProduct2 implements Product {    public void show() {        System.out.println("具体产品2显示...");    }}//抽象工厂:提供了厂品的生成方法interface AbstractFactory {    public Product newProduct();}//具体工厂1:实现了厂品的生成方法class ConcreteFactory1 implements AbstractFactory {    public Product newProduct() {        System.out.println("具体工厂1生成-->具体产品1...");        return new ConcreteProduct1();    }}//具体工厂2:实现了厂品的生成方法class ConcreteFactory2 implements AbstractFactory {    public Product newProduct() {        System.out.println("具体工厂2生成-->具体产品2...");        return new ConcreteProduct2();    }}

复制代码

import javax.xml.parsers.*;import org.w3c.dom.*;import java.io.*;/** * @author hz * @version 1.0 */public class ReadXML {    //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象    public static Object getObject() {        try {            //创建文档对象            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();            DocumentBuilder builder = dFactory.newDocumentBuilder();            Document doc;            doc = builder.parse(new File("src/FactoryMethod/config1.xml"));            //获取包含类名的文本节点            NodeList nl = doc.getElementsByTagName("className");            Node classNode = nl.item(0).getFirstChild();            String cName = "FactoryMethod." + classNode.getNodeValue();            //System.out.println("新类名:"+cName);            //通过类名生成实例对象并将其返回            Class<?> c = Class.forName(cName);            Object obj = c.newInstance();            return obj;        } catch (Exception e) {            e.printStackTrace();            return null;        }    }
}

复制代码

那么该模式有哪些优缺点呢?

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点:

  • 类的个数容易过多,增加复杂度
  • 增加了系统的抽象性和理解难度
  • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
总结:

对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

对于某个产品,调用者清楚地知道应该使用哪个具体工厂服务,实例化该具体工厂,生产出具体的产品来,那么我们可以采用工厂方法模式,如 JAVA 中的 Colletion 中的 iterator() 方法;

或者我们只是需要一种产品,而不想知道也不需要知道究竟是哪个工厂为生产的,即最终选用哪个具体工厂的决定权在生产者一方,它们根据当前系统的情况来实例化一个具体的工厂返回给使用者,而这个决策过程这对于使用者来说是透明的,我们也可以选用工厂方法模式。

但是注意当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式。

不管是简单工厂模式还是我们的工厂方法模式考虑的都是一系列相同类型的产品,如果生产的产品类型不是一个类型,那我们怎么处理呢,这就需要我们的工厂模式的另一种模式-抽象工厂模式。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/2999609ac38f866e5c9cbac9d
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券