Java设计模式 —— 工厂模式

3 简单工厂模式

3.1 创建型模式

Creational Pattern 关注对象的创建过程,对类的实例化过程进行了抽象,将软件模块中对象的创建和对象的使用分离,对用户隐藏了类的实例的创建细节。创建型模式描述如何将对象的创建和使用分离,让用户在使用对象时无须关心对象的创建细节,从而降低系统的耦合度。

3.2 简单工厂模式

Simple Factory Pattern :定义一个工厂类,它可以根据参数的不同返回不同的实例,被创建的实例通常都具有共同的父类。

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

3.3 简单工厂模式实现

3.3.1 抽象产品类

public abstract class Product {
  public void methodSame() {
    // 产品公共业务实现
  }
  
  // 抽象业务方法,由子类实现
  public abstract void methodDiff();
}

3.3.2 具体产品类

public class ConcreteProductA extends Product{
  public void methodDiff() {
    // 具体产品的业务实现
  }
}


public class ConcreteProductB extends Product{
  public void methodDiff() {
    // 具体产品的业务实现
  }
}

3.3.3 工厂类

工厂类是简单工厂模式的核心,没有工厂类之前客户端一般通过 new 关键字直接创建产品实例,而引入工厂类之后客户端可以通过工厂类来创建产品实例。

简单工厂模式提供一个静态工厂方法给客户端使用,根据传入的参数创建产品对象。

public class Factory {
  // 静态工厂方法
  public static Product getProduct(String arg) {
    Product product = null;
    if (arg.equals("A")) {
      product = new ConcreteProductA();
    }
    else if (arg.equals("B")) {
      product = new ConcreteProductB();
    }
    
    return product;
  }
}

3.3.4 客户端调用

public class Client {
  public static void main(String[] args) {
    Product product = Factory.getProduct("A");
    product.methodSame();
    product.methodDiff();
  }
}

3.4 简单工厂模式优/缺点

简单工厂模式的优点如下:

  • 可以决定在什么时候创建哪一个产品类的实例,客户端免除直接创建产品对象的职责,而仅仅使用产品,实现了对象的创建和使用分离。
  • 客户端无须知道所创建的具体产品类,只需要知道对应的参数即可。
  • 引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,提高系统的灵活性。

简单工厂模式的缺点如下:

  • 工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统将受到影响。
  • 使用简单工厂模式会增加系统中类的个数,增加了系统的复杂性。
  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,不利于系统的扩展和维护。
  • 简单工厂模式使用的是静态工厂方法,造成工厂角色无法被继承。

4 工厂方法模式

4.1 工厂方法模式概述

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

使用简单工厂模式设计工厂类,在系统中增加一种新的产品类型,需要修改工厂类的原有代码逻辑,违背开闭原则。

现在对上述工厂类做一次重构,不再提供一个工厂类统一负责所有产品的创建,而是将具体产品的创建过程交给专门的工厂子类完成。这种实现方式使得在不修改具体工厂类的情况下引进新的产品类型(只需要为新的产品类型定义一个具体的工厂子类即可)。

工厂方法模式结构图如下所示:

4.2 工厂方法模式实现

4.2.1 抽象产品接口

public interface Product {
  public void method();
}

4.2.2 具体产品类

public class ConcreteProductA implements Product{
  public void method() {
    // 业务代码实现
  }
}


public class ConcreteProductB implements Product{
  public void method() {
    // 业务代码实现
  }
}

4.2.3 抽象工厂接口

与简单工厂模式相比,工厂方法模式最重要的特定是引入了抽象工厂角色。

public interface Factory {
  public Product createProduct();
}

4.2.4 具体工厂子类

public class ConcreteFactoryA implements Factory {
  public Product createProduct(){
    return new ConcreteProductA();
  }
}


public class ConcreteFactoryB implements Factory {
  public Product createProduct(){
    return new ConcreteProductB();
  }
}

4.2.5 客户端调用

Factory factory = new ConcreteFactoryA();  // 可通过配置文件与反射机制实现
factory.createProduct();

可以通过配置文件存储具体工厂类的类名,再通过反射机制创建具体工厂对象。

4.3 反射机制与配置文件

4.3.1 Java 反射机制

Java反射机制是指在程序运行时获取已知名称的类或已有对象的相关信息,包括类的方法、属性、父类等。在反射中使用最多的类是Class ,Class 类的实例表示正在运行的 Java 应用程序中的类和接口,其 forName(String className) 方法可以返回指定的 Class 对象,再通过 Class 对象的 newInstance() 方法创建此对对象代表的类的一个实例,即可以通过类名创建该类的实例对象。

Class c = Class.forName("java.lang.String");
Object obj = c.newInstance();
return obj;

4.3.2 配置文件

在软件开发中可以把类名存储到XML配置文件,再读取配置文件获取类名字符串,然后通过 Java 反射机制来创建对象。

<config>
	<className>designpatterns.factorymethod.ConcreteFactoryA
    </className>
</config>

读取配置文件

import javax.xml.parse.*;
import org.w3c.dom.*;
import java.io.*;

public class XMLUtil {
  public static Object getBean(String path) {
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder = factory.newDocumentBuilder();
      Document doc = builder.parse(new File(path));
      
      // Get node that include classname
      NodeList nl = doc.getElementByTagName("className");
      Node node = nl.item(0).getFirstChild();
      String className = node.getNodeValue();
      
      Class c = Class.forName(className);
      Object obj = c.newInstance();
      return objl
    }
    catch(Exception e) {
      e.printStackTrace();
      return null;
    }
  }
}

4.3.3 重写客户端调用

Factory factory = (Factory)XMLUtil.getBean("config.xml");
factory.createProduct();

4.4 工厂方法模式优/缺点

工厂方法模式是简单工厂模式的延申,它继承了简单工厂模式的优点,同时还弥补了简单工厂的不足。工厂方法模式是使用频率最高的模式之一。

工厂方法模式优点如下:

  • 工厂方法用于创建客户需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建的细节。
  • 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。工厂方法模式之所以又被称为”多态工厂模式“,正因为所有工厂类都具有同一抽象父类。
  • 工厂方法模式在系统中加入新产品时无须修改原有的代码,只需新增新的具体工厂类和具体产品类,系统扩展性非常好。

工厂方法模式缺点如下:

  • 增加新的产品时,系统中类的个数将成对增加,一定程度增加了系统的复杂性。
  • 引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。

5 抽象工厂模式

5.1 产品等级结构与产品族

5.1.1 产品等级结构

产品等级结构即产品的继承结构,例如一个产品抽象类是电视机,其子类有海尔电视机、海信电视机和TCL电视机,则抽象电视机和具体电视机之间构成一个产品等级结构

5.1.2 产品族

在抽象工厂模式中,产品族是指由同一个工厂生产的位于不同产品等级结构中的一组产品。例如海尔电器工厂生产的海尔电视机和海尔冰箱,海尔电视机位于电视机产品结构中,海尔冰箱位于冰箱产品结构中,海尔电视机和海尔冰箱构成了一个产品族。

5.2 抽象工厂模式概述

Abstract Factory Pattern:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

工厂方法模式引入工厂等级结构解决了简单工厂模式中工厂类职责太重的问题,但由于每个具体工厂都只有一个或一组重载的工厂方法,只能生产一种产品,可能会导致系统中存在大量的工厂类,增加系统的开销。

抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式只针对一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中产品对象的创建。

抽象工厂模式为创建一组对象提供了一种解决方案,其负责创建一族产品。

抽象工厂模式结构图如下:

5.3 抽象工厂模式实现

5.3.1 抽象产品接口

public interface AbstractProductA() {
  public void method1();
}


public interface AbstractProductB() {
  public void method2();
}

5.3.2 具体产品类

public class ConcreteProductA1 implements AbstractProductA{
  public void method1() {
    // 业务代码实现
  }
}


public class ConcreteProductA2 implements AbstractProductA{
  public void method1() {
    // 业务代码实现
  }
}


public class ConcreteProductB1 implements AbstractProductB{
  public void method2() {
    // 业务代码实现
  }
}


public class ConcreteProductB2 implements AbstractProductB{
  public void method2() {
    // 业务代码实现
  }
}

5.3.3 抽象工厂接口

public interface AbstractFactory {
  public AbstractProductA createProductA();
  public AbstractProductB createProductB();
}

5.3.4 具体工厂类

public class ConcreteFactory1 implements AbstractFactory{
  // 工厂方法一
  public AbstractProductA createProductA() {
    return new ConcreteProductA1();
  }
  
  // 工厂方法二
  public AbstractProductB createProductB() {
    return new ConcreteProductB1();
  }
}


public class ConcreteFactory2 implements AbstractFactory{
  // 工厂方法一
  public AbstractProductA createProductA() {
    return new ConcreteProductA2();
  }
  
  // 工厂方法二
  public AbstractProductB createProductB() {
    return new ConcreteProductB2();
  }
}

5.3.5 客户端调用

public class Client {
  // 从XML文件读取要创建的工厂类
  AbstractFactory factory = (AbstractFactory)XMLUtil.getBean();
  AbstractProductA productA = factory.createProductA();
  AbstractProductB productB = factory.createProductB();
  
  productA.method1();
  productB.method2();
}

5.4 开闭原则的倾斜性

开闭原则要求对扩展开发,对修改关闭,通过扩展达到增强功能的目的,对于涉及多个产品族和产品等级结构的系统,其功能扩展包括两个方面。

  • 增加产品族
  • 增加产品等级结构

在抽象工厂模式中增加新的产品族很方便,只需增加一族新的具体组件并对应一个新的具体工厂,原有代码无须修改,符合开闭原则。但是在抽象工厂模式中增加新的产品结构很麻烦,需要先修改抽象工厂接口,新增声明创建新产品的方法,然后逐个修改具体工厂类,增加想应方法以实现在具体工厂创建新产品,不符合开闭原则。抽象工厂模式的这种性质被称为开闭原则的倾斜性

正因为抽象工厂模式存在开闭原则的倾斜性,它以一种倾斜的方式来满足开闭原则,为增加新产品族提供方便,但不能为增加新产品结构提供方便,因此要求设计人员在设计之初就能考虑全面,不会在设计完成之后再向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则会导致大量的代码修改,降低系统的可维护性。

5.5 抽象工厂模式优/缺点

抽象工厂模式的优点主要如下:

  • 增加新的产品族和方便,无须修改现有系统。
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
  • 抽象工厂模式隔离了具体类的生成,这种隔离使得更换一个具体工厂变得相对容易。

抽象工厂模式的缺点主要如下:

  • 增加新的产品等级结构很麻烦,不符合开闭原则。
posted @ 2022-09-30 15:36  ylyzty  阅读(125)  评论(0编辑  收藏  举报