工厂设计模式

工厂设计模式-LMLPHP

概述

工厂设计模式(Factory Design Pattern) 是一种创建型设计模式,它提供了一种创建对象的最佳方式。工厂模式将实例化对象的过程推迟到子类,从而实现了对象创建的解耦。这种模式有助于代码的可维护性和可扩展性,特别是在需要创建复杂对象或者依赖于具体类的情况下。

工厂模式主要有以下几种形式:

  1. 简单工厂模式(Simple Factory Pattern):又称静态工厂方法(Static Factory Method),通过一个静态方法根据传入参数的不同返回不同类的实例。
  2. 工厂方法模式(Factory Method Pattern):定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类。
  3. 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

简单工厂模式

简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。

这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。因为客户类往往有多个,如果不使用简单工厂,所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。

类图

以下为简单工厂的UML模式图,在这个类图中,Client 不直接与具体产品类(ConcreteProductAConcreteProductB)交互,而是通过 Factory 类来进行。这样做的优点是客户端从具体产品的创建过程中解耦,提高了程序的扩展性和可维护性。

工厂设计模式-LMLPHP

代码实现

public interface Product {
    void use();
}
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using ConcreteProductA");
    }
}
public class ConcreteProductB implements Product {

    @Override
    public void use() {
        System.out.println("Using ConcreteProductB");
    }
}

以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-06-27 13:53
 * @description : 简单工厂类
 */
public class SimpleFactory {
    public static Product createProduct(String type) {
        if (type.equals("A")) {
            return new ConcreteProductA();
        } else if (type.equals("B")) {
            return new ConcreteProductB();
        }
        return null;
    }
}
/**
 * @author : Leo
 * @version 1.0
 * @date 2024-06-27 13:49
 * @description :
 */
public class Main {
    public static void main(String[] args) {
        Product product = SimpleFactory.createProduct("A");
        product.use();
    }
}

工厂设计模式-LMLPHP

认识简单工厂

简单工厂的功能

工厂嘛,就是用来造东西的。在Java里面,通常情况下是用来创建接口的,但是也可以创建抽象类,甚至是一个具体的类实例。一定要注意,虽然前面的示例是利用简单工厂来创建的接口,但是也是可以用简单工厂来创建抽象类或者是普通类的实例的

静态工厂

使用简单工厂的时候,通常不用创建简单工厂类的类实例,没有创建实例的必要。因此可以把简单工厂类实现成一个工具类,直接使用静态方法就可以了,也就是说简单工厂的方法通常都是静态的,所以也被称为静态工厂。如果要防止客户端无谓的创造简单工厂实例,还可以把简单工厂的构造方法私有化了

万能工厂

一个简单工厂可以包含很多用来构造东西的方法,这些方法可以创造不同的接口、抽象类或者是类实例,一个简单工厂理论上可以构造任何东西,所以又称之为“万能工厂”

简单工厂的调用顺序示意图

工厂设计模式-LMLPHP

可配置的简单工厂

现在已经学会通过简单工厂来选择具体的实现类了,现在有一个疑问点。比如:在现在的实现中,再新增加一种实现,会怎样呢?

那就需要修改工厂类,才能把新的实现添加到现有系统中。比如现在新加了一个实现 ConcreteProductC ,那么需要类似下面这样来修改工厂类:

工厂设计模式-LMLPHP

每次新增加一个实现类都来修改工厂类的实现,那岂不是又有了耦合嘛,而且还违反单一原则。肯定不是我们希望的实现方式。那么现在希望新增加了实现类过后不修改工厂类,该怎么办呢?

一个解决的方法就是使用配置文件,当有了新的实现类过后,只要在配置文件里面配置上新的实现类就好了,在简单工厂的方法里面可以使用反射,当然也可以使用IoC/DI(控制反转/依赖注入,这个不在这里讨论)来实现。

看看如何使用反射加上配置文件,来实现添加新的实现类过后,无须修改代码,就能把这个新的实现类加入应用中。

  1. 配置文件用最简单的properties文件,实际开发中多是xml配置。定义一个名称为 factory-test.properties的配置文件,放置到Factory同一个包下面,内容如下:
ImplClass=org.leocoder.simple.product.ConcreteProductC
  1. 此时的工厂类实现如下:
/**
       * 具体的创造Api的方法,根据配置文件的参数来创建接口
       * @return 创造好的Api对象
       */  
      public static Product createProduct(){  
          //直接读取配置文件来获取需要创建实例的类   
          Properties p = new Properties();  
          InputStream in = null;  
          try {  
              in = Factory.class.getResourceAsStream("factory-test.properties");  
              p.load(in);  
          } catch (IOException e) {  
              System.out.println("装载工厂配置文件出错了,具体的堆栈信息如下:");  
              e.printStackTrace();  
          }finally{  
              try {  
                  in.close();  
              } catch (IOException e) {  
                  e.printStackTrace();  
              }  
          }  
          //用反射去创建,那些例外处理等完善的工作这里就不做了  
          Product product = null;  
          try {  
              product = (Product)Class.forName(p.getProperty("ImplClass")).newInstance();  
          } catch (InstantiationException e) {  
              e.printStackTrace();  
          } catch (IllegalAccessException e) {  
              e.printStackTrace();  
          } catch (ClassNotFoundException e) {  
              e.printStackTrace();  
          }  
          return product;  
      }  

此时就已经完全做到了解耦,即使我们中途业务需要扩展也可以轻松实现。

工厂方法模式

概述

工厂方法模式(Factory Method Pattern) 是一种创建型设计模式,它提供了一个创建对象的方法,但实际的工作将由子类完成,这样的设计模式可以让类的实例化推迟到其子类中进行。这种模式通过定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类,从而使代码管理更加简单,同时也扩展了系统的可能功能。

通过工厂方法模式,我们可以将对象的创建过程封装在工厂类中,客户端只需要调用工厂类的方法,而不需要知道具体的实现类。这种方式提高了代码的灵活性和可扩展性。

工厂方法模式包含以下几个主要角色:

  1. 抽象产品(Product):定义了产品的接口,是工厂方法模式创建的对象类型。
  2. 具体产品(ConcreteProduct):实现了抽象产品接口的具体类,是被工厂方法创建的对象。
  3. 抽象工厂(Creator):声明了返回产品对象的工厂方法,即 createProduct,该方法返回一个抽象产品对象。可以包含一些具体的方法。
  4. 具体工厂(ConcreteCreator):实现了抽象工厂中的工厂方法,返回具体产品的实例。

类图

下面的类图描述了工厂方法模式的结构:

工厂设计模式-LMLPHP

代码实现

public abstract class Factory_ {
    public abstract Product createProduct();
}
public class FactoryA extends Factory_ {
    @Override
    public Product createProduct() {
        return new ProductA();
    }
}
public class FactoryB extends Factory_ {
    @Override
    public Product createProduct() {
        return new ProductB();
    }
}
public abstract class Product {
    public abstract void use();
}
public class ProductA extends Product {
    @Override
    public void use() {
        System.out.println("使用产品A");
    }
}
public class ProductB extends Product {
    @Override
    public void use() {
        System.out.println("使用产品B");
    }
}

客户端使用:客户端代码通过调用工厂方法来获取产品对象,而无需知道具体的产品类。

public class Main {
    public static void main(String[] args) {
        // 创建工厂A和产品A
        Factory_ factoryA = new FactoryA();
        Product productA = factoryA.createProduct();
        productA.use(); // 输出:使用产品A

        // 创建工厂B和产品B
        Factory_ factoryB = new FactoryB();
        Product productB = factoryB.createProduct();
        productB.use(); // 输出:使用产品B
    }
}

通过这种方式,工厂方法模式使得添加新产品类时,只需添加一个具体的创建者而无需修改现有代码,符合开闭原则,提高了代码的可维护性和扩展性。

与简单工厂加个产品需要改一揽子的代码相比,工厂方法只需要创建一个新的创建者类,客户端调用时ConcreteCreatorA更换为新类名就可以。

适用场景

工厂方法模式适用于以下场景:

  1. 当一个类不知道它所需要的对象的类时:工厂方法模式可以使一个类的实例化延迟到子类。
  2. 当一个类希望由它的子类来指定它所创建的对象时:通过工厂方法模式,基类将创建对象的责任交给子类。
  3. 当类将创建对象的职责委托给多个帮助子类中的一个:工厂方法模式通过多态性帮助将对象的创建延迟到子类。

抽象工厂模式

概述

抽象工厂模式(Abstract Factory Pattern) 是创建型设计模式中的一种,它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。这种模式是工厂方法模式的一种扩展,它用于创建产品家族,而不仅仅是单一产品。通过这种方式,可以增强程序的灵活性和可维护性,同时也支持良好的封装性和扩展性。

抽象工厂一般分为以下几个角色:

  1. 抽象工厂(AbstractFactory):声明创建一组产品对象的方法,每个方法对应一个产品。
  2. 具体工厂(ConcreteFactory):实现抽象工厂的接口,具体实现创建一组具体产品的方法。
  3. 抽象产品(AbstractProduct):为每种产品声明一个接口。
  4. 具体产品(ConcreteProduct):实现抽象产品接口的具体类。
  5. 客户端(Client):使用抽象工厂来创建具体产品对象。

类图

以下类图清晰展示了抽象工厂模式的结构和组件之间的关系:

工厂设计模式-LMLPHP

认识抽象工厂

模式的功能

抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口,一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。

从某种意义上看,抽象工厂其实是一个产品系列,或者是产品簇。上面例子中的抽象工厂就可以看成是工厂簇,每个不同的工厂方案,代表一种具体的产品系列。

实现接口

AbstractFactory在Java中通常实现成为接口,大家不要被名称误导了,以为是实现成为抽象类,当然,如果需要为这个产品簇提供公共的功能,也不是不可以把AbstractFactory实现成为抽象类,但一般不这么做。

使用工厂方法

AbstractFactory 定义了创建产品所需要的接口,具体的实现是在实现类里面,通常在实现类里面就需要选择多种更具体的实现,所以AbstractFactory定义的创建产品的方法可以看成是工厂方法,而这些工厂方法的具体实现就延迟到了具体的工厂里面。也就是说 使用工厂方法来实现抽象工

抽象工厂模式的调用顺序示意图

工厂设计模式-LMLPHP

应用场景

抽象工厂模式适用于以下场景:

  1. 系统需要独立于其产品创建的方式:系统不关心产品的创建过程,只关心产品的接口。
  2. 系统需要多个产品族中的一个产品:一个产品族中的产品通常有多个种类,并且需要一起使用。
  3. 系统的产品类非常稳定:在系统中添加新产品族时,不需要修改现有系统的代码。

三种模式的异同

首先,我们来看三种工厂模式的定义:

  • 简单工厂模式:最简单的工厂模式,适合产品种类较少且不经常变化的情况。工厂类集中处理所有产品的创建逻辑,这简化了代码但降低了系统的扩展性。
  • 工厂方法模式:允许多个创建者子类来实现具体的产品创建。这种分散的创建过程提高了系统的灵活性,并且增强了代码的可扩展性。
  • 抽象工厂模式:提供一个创建一系列相关或互依赖对象的接口,而无需指定它们具体的类。适用于处理多个系列产品的情况,每个具体工厂都能生产一个完整的产品家族。

接着,我们用一张表来总结三种工厂模式的特性差异:

工厂模式总结

工厂模式是设计模式中非常关键的一类,主要用于解决对象创建过程中的复杂性,并帮助将对象的实例化过程与使用者分离,从而增加系统的封装性和灵活性。通过定义一个创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化推迟到其子类。这种模式不仅便于管理和维护大型软件系统中的对象创建问题,还使系统更加模块化,易于理解和扩展。总的来说,工厂模式在需要生成复杂对象时,提供了极大的便利和高效的管理与维护策略。

06-28 09:43