Java设计模式--简单工厂、工厂方法和抽象工厂之间的区别

简介: 设计模式中的工厂模式(Factory Design pattern)是一个比较常用的创建型设计模式,其中可以细分为三种:简单工厂(Simple Factory)、工厂方法(Factory Method)和抽象工厂(Abstract Factory)。那么三者有什么区别呢?

设计模式中的工厂模式(Factory Design pattern)是一个比较常用的创建型设计模式,其中可以细分为三种:简单工厂(Simple Factory)、工厂方法(Factory Method)和抽象工厂(Abstract Factory)。那么三者有什么区别呢?先说结论:

  • 简单工厂:只有唯一工厂(简单工厂),一个产品接口/抽象类,根据简单工厂中的静态方法来创建具体产品对象。适用于产品较少,几乎不扩展的情景
  • 工厂方法:有多个工厂(抽象工厂+多个具体工厂),一个产品接口/抽象类,根据继承抽象工厂中的方法来多态创建具体产品对象。适用于一个类型的多个产品
  • 抽象方法:有多个工厂(抽象工厂+多个具体工厂),多个产品接口/抽象类,对产品子类进行分组,根据继承抽象工厂中的方法多态创建同组的不同具体产品对象。适用于多个类型的多个产品

下面具体展开说明

一、简单工厂模式(Simple Factory Pattern)

1.1 简单工厂模式介绍

简单工厂模式又叫做静态工厂方法模式(static Factory Method pattern),它是通过使用静态方法接收不同的参数来返回不同的实例对象。我们通过一个类图来进行讲解:

  • Product接口:定义要创建的产品对象的接口
  • ProductAProductBProductC产品类:实现产品接口,具有产品接口特性的具体产品
  • SimpleFactory简单工厂:只有一个工厂,通过静态方法createProduct创建具体的产品对象
  • client客户端:客户端有多个,每个客户端可以通过简单工厂来创建具体的产品对象

1.2 简单工厂模式实现

我们以上面类图为例,实现简单工厂模式:

/**产品接口**/
public interface Product {
   
   

    void doSomething();
}

/**具体产品实现**/
class ProductA implements Product{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是ProductA");
    }
}

class ProductB implements Product{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是ProductB");
    }
}

class ProductC implements Product{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是ProductC");
    }
}
/**简单工厂**/
public class SimpleFactory {
   
   
    /**工厂类创建产品静态方法**/
    public static Product createProduct(String productName) {
   
   
        Product instance = null;
        switch (productName){
   
   
            case "A":
                instance = new ProductA();
                break;
            case "B":
                instance = new ProductB();
                break;
            case "C":
                instance = new ProductC();
        }
        return instance;
    }
    /**客户端(client)调用工厂类**/
    public static void main(String[] args) {
   
   
        SimpleFactory simpleFactory = new SimpleFactory();
        createProduct("A").doSomething();
        createProduct("B").doSomething();
    }
}
  • 优点:简单工厂可以使客户端免除直接创建对象的职责,能够根据需要创建出对应的产品。实现客户端和产品类代码分离。此外可以通过配置文件来实现不修改客户端代码的情况下添加新的具体产品类(改进)。
  • 缺点:违背开闭原则,如果需要新增其他产品类,就必须在工厂类中新增if-else逻辑判断(可以通过配置文件来改进)。但是整体来说,系统扩展还是相对其他工厂模式要困难。
    我们发现,简单工厂模式中的工厂类使用的是静态方法,那么为什么要这样做呢?可不可以使用非静态的方法呢?

  • 使用静态方法可以不需要使用new的方式创建对象,方便调用

  • 静态方法意味着可以直接获得实例对象,非静态方法只能通过构造方法(一般私有)调用,在工厂类以外不能被访问
  • 对于一些实例化和销毁对象比较敏感的场景,比如数据库连接池,实例化对象能够重复稳定的被使用

综上来说,简单工厂模式适用于业务简单,产品固定不会经常改变工厂类的情况。

1.3 简单工厂模式使用场景

下面来看看简单工厂模式一般用于哪些业务场景

1.3.1 JDK 、Spring等各类源码

在Java 中就有这样的设计,比如DateFormat中的这个方法就是简单工厂的应用

private static DateFormat get(LocaleProviderAdapter adapter, int timeStyle, int dateStyle, Locale loc) {
   
   
    DateFormatProvider provider = adapter.getDateFormatProvider();
    DateFormat dateFormat;
    //逻辑判断实现那个具体对象
    if (timeStyle == -1) {
   
   
        dateFormat = provider.getDateInstance(dateStyle, loc);
    } else {
   
   
        if (dateStyle == -1) {
   
   
            dateFormat = provider.getTimeInstance(timeStyle, loc);
        } else {
   
   
            dateFormat = provider.getDateTimeInstance(dateStyle, timeStyle, loc);
        }
    }
    return dateFormat;
}

此外还有Calender等,在Spring 源码中也可以看到一些以"Factory"结尾的类,这些都是工厂模式的使用。

1.3.2 数据库连接池

比如在业务连接数据库时,需要支持不同的数据库,比如有dbcpc3p0druid等等,这个时候数据库连接方式有限,而且比较固定不容易更改,所以可以尝试采用简单工厂模式来进行管理数据库连接对象。

二、工厂方法模式(Factory Method Pattern)

我们知道简单工厂模式有违背开闭原则,不容易扩展的缺点,所以在 GOF 23种设计模式中也没有简单工厂模式,下面我们就来看看另外一种工厂模式:工厂方法模式

2.1 工厂方法模式介绍

抽象工厂模式所要解决的问题是在一个产品族上,若存在多个不同类型的产品情况下,接口选择的问题。

工厂方法模式实际上是简单工厂模式的升级,工厂方法模式定义除了产品接口外,还定义了一个用于创建对象工厂的接口,让工厂子类再去实例化对应的产品类。通过类图来解释:

  • Product接口:和简单工厂相同,提供产品对象的接口
  • ProductAProductBproductC:具体类型的产品对象
  • FactoryAFactoryBFactoryC:具体的产品工厂,实现具体的产品对象
  • AbstractFactory:抽象工厂,可以有多个,其中的方法负责返回创建的产品对象
  • Client:使用该模式的客户端

2.2 工厂方法模式实现

对照着上面的类图,我们可以对应实现相应的代码:

/**产品接口**/
public interface Product {
   
   

    void doSomething();
}

/**具体产品实现**/
class ProductA implements Product{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是ProductA");
    }
}

class ProductB implements Product{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是ProductB");
    }
}

class ProductC implements Product{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是ProductC");
    }
}

/**工厂接口**/
public interface AbstractFactory {
   
   
    /**创建Product方法,区别与工厂模式的静态方法**/
    public Product createProduct();
}

/**具体工厂实现**/
class FactoryA implements AbstractFactory{
   
   

    @Override
    public Product createProduct() {
   
   
        return new ProductA();
    }
}

class FactoryA implements AbstractFactory{
   
   

    @Override
    public Product createProduct() {
   
   
        return new ProductA();
    }
}

class FactoryA implements AbstractFactory{
   
   

    @Override
    public Product createProduct() {
   
   
        return new ProductA();
    }
}
/**客户端调用工厂**/
public class Client {
   
   
    public static void main(String[] args) {
   
   
        Product productA = new FactoryA().createProduct();
        productA.doSomething();
        Product productB = new FactoryB().createProduct();
        productB.doSomething();
    }
}

其中最主要的是 AbstractFactory类中的createProduct方法,通过这个方法来生成具体产品,这也是为什么叫工厂方法的原因。和简单工厂的静态方法不同,这里是使用的非静态调用方式。而且可以发现,没有了简单工厂中的 if-else逻辑判断,相对而言扩展性也要强的多。

  • 优点:完全实现开闭原则,实现了可扩展和更复杂的层次结构。明确了职责,具有多态性,适用于任何实体类。
  • 缺点:如果业务增加,会使得系统中类的个数成倍增加,提高了代码的复杂度

2.3 工厂方法模式使用场景

2.3.1 Slf4j

在Slf4j 这个我们经常使用的日志框架中,就有工厂方法模式的应用,比如使用频率很高的获取logger对象实例中:

private Logger logger = LoggerFactory.getLogger(Client.class);

点进源码看我们会发现这个getLogger方法:

//简单工厂模式
public static Logger getLogger(String name) {
   
   
    /**工厂方法模式的使用**/
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}
//工厂接口
public interface ILoggerFactory {
   
   
    Logger getLogger(String var1);
}
//Logger产品接口
public interface Logger {
   
   
    String ROOT_LOGGER_NAME = "ROOT";
    ...
}

需要调用工厂方法接口来实现具体logger 对象实例,这就是一个工厂方法模式的一个典型应用

2.3.2 一些规则配置解析

在一些需要不同类型的规则配置解析时,我们也可以用到工厂方法模式,比如引用《设计模式之美》的代码:

public class RuleConfigSource {
   
   
  public RuleConfig load(String ruleConfigFilePath) {
   
   
    String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);

    IRuleConfigParserFactory parserFactory = RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension);
    if (parserFactory == null) {
   
   
      throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath);
    }
    IRuleConfigParser parser = parserFactory.createParser();

    String configText = "";
    //从ruleConfigFilePath文件中读取配置文本到configText中
    RuleConfig ruleConfig = parser.parse(configText);
    return ruleConfig;
  }

  private String getFileExtension(String filePath) {
   
   
    //...解析文件名获取扩展名,比如rule.json,返回json
    return "json";
  }
}

//因为工厂类只包含方法,不包含成员变量,完全可以复用,
//不需要每次都创建新的工厂类对象,所以,简单工厂模式的第二种实现思路更加合适。
public class RuleConfigParserFactoryMap {
   
    //工厂的工厂
  private static final Map<String, IRuleConfigParserFactory> cachedFactories = new HashMap<>();

  static {
   
   
    cachedFactories.put("json", new JsonRuleConfigParserFactory());
    cachedFactories.put("xml", new XmlRuleConfigParserFactory());
    cachedFactories.put("yaml", new YamlRuleConfigParserFactory());
    cachedFactories.put("properties", new PropertiesRuleConfigParserFactory());
  }

  public static IRuleConfigParserFactory getParserFactory(String type) {
   
   
    if (type == null || type.isEmpty()) {
   
   
      return null;
    }
    IRuleConfigParserFactory parserFactory = cachedFactories.get(type.toLowerCase());
    return parserFactory;
  }
}

在需要添加新的规则配置解析器时,只需要创建新的 parser 类和 parserfactory 完成不同的配置

三、抽象工厂模式(Abastract Factory Pattern)

抽象工厂模式没有简单工厂和工厂方法模式那么常用,场景比较特殊,在简单工厂和工厂方法中,对于类只有一种分类方式,比如简单工厂中,根据产品类型分为ProductAProductBProductC。但是如果有多种分类方式,比如按照产品的生产商分类,ProductA可能和ProductC为一类。这样就用到了抽象工厂模式

3.1 抽象工厂模式介绍

抽象工厂模式(Abstract Factory Pattern)属于创建型模式,它实际上是对工厂方法模式的扩展,相当于一个超级工厂,用于创建其他工厂的模式。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,而且每个工厂都能按照工厂模式提供对象。其实抽象工厂也是为了减少工厂方法中的子类和工厂类数量,基于此提出的设计模式,如下图(来源淘系技术):

比如在工厂方法中,我们只能按照键盘、主机、显示器分别进行分类,这样会造成大量的工厂类和产品子类。而抽象工厂可以将上述三种产品类进行分组,可以大大减少工厂类的数量。我们再来看看对应的类图:

  • Product1Product2:定义一种类型的产品对象接口
  • Product1AProduct1B等:各种类型的具体产品对象
  • FactoryAFactoryB:具体产品工厂,负责创建该工厂类型下的产品对象
  • AbstractFactory:抽象工厂接口,定义一类产品对象
  • Client:客户端,使用抽象工厂,调用产品对象

3.2 抽象工厂模式实现

下面就根据上面的类图,利用代码实现抽象工厂:

/**Product1类的产品接口**/
public interface Product1 {
   
   
    void doSomething();
}

class Product1A implements Product1{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是Product1A");
    }
}

class Product1B implements Product1{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是Product1B");
    }
}


/** Product2类的产品接口**/
public interface Product2 {
   
   
    void doSomething();
}

class Product2A implements Product1{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是Product2A");
    }
}

class Product2B implements Product1{
   
   

    @Override
    public void doSomething() {
   
   
        System.out.println("我是Product2B");
    }
}

/**抽象工厂接口**/
public interface AbstractFactory {
   
   

    public Product1 createProduct1();

    public Product2 createProduct2();
}

/**A类工厂**/
public class FactoryA implements AbstractFactory{
   
   

    @Override
    public Product1 createProduct1() {
   
   
        return new Product1A();
    }

    @Override
    public Product2 createProduct2() {
   
   
        return new Product2A();
    }
}

/**B类工厂**/
public class FactoryB implements AbstractFactory{
   
   

    @Override
    public Product1 createProduct1() {
   
   
        return new Product1B();
    }

    @Override
    public Product2 createProduct2() {
   
   
        return new Product2B();
    }
}


/**Client客户端调用**/
public class Client {
   
   
    public static void main(String[] args) {
   
   
        new FactoryA().createProduct1().doSomething();
        new FactoryB().createProduct2().doSomething();
    }
}
  • 优点:增加分组比较容易,而且能大大减少工厂类的数量
  • 缺点:因为分组,所以分组中的产品扩展就比较困难,比如再新增一个Product3,就需要改动AbstractFactoryFactoryAFactoryB几乎所有工厂类

综上,没有哪种方法是万金油,要针对业务场景来使用哪种工厂模式

参考资料

https://www.zhihu.com/question/27125796/answer/1615074467

《重学设计模式》

https://www.cnblogs.com/sunweiye/p/10815928.html

https://time.geekbang.org/column/article/197254

目录
相关文章
|
17天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
14天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
22天前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
22天前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
36 0
[Java]23种设计模式
|
6天前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
1月前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
23 1
|
1月前
|
Java 编译器 C语言
【一步一步了解Java系列】:探索Java基本类型与C语言的区别
【一步一步了解Java系列】:探索Java基本类型与C语言的区别
42 2
|
1月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
34 1
|
25天前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
27天前
|
缓存 算法 Java
Java 中线程和纤程Fiber的区别是什么?
【10月更文挑战第14天】
59 0

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    54
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    62
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    57
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    41
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    106
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78