设计模式-Factory

抽象工厂+工厂方法

问题

创建型模式可以解耦客户端和对象的创建过程,Builder 是解决了"复杂对象的创建问题",Factory 则是让"具体对象的创建过程对客户端透明"。

客户端不需要知道自己创建的是哪一个具体类对象,而是交由 Factory 来实例化,其实现方法是:定义一个用于创建对象的接口,让接口的实现类来决定实例化具体的对象。

分类

Factory 一共有三种模式:

  • 简单工厂:不在 23 种设计模式中
  • 工厂方法:每个具体类的创建过程对应一个创建方法
  • 抽象工厂:具体类的每种组合对应一个创建方法

示例和组成

简单工厂

简单工厂只有一个 Factory 对象,提供一个创建方法,通过传入不同的参数,返回不同的具体类对象,UML 和示例代码如下:

图 1. 简单工厂的 UML

简单工厂的代码示例:

java 复制代码
public interface Product {
	void print();
}

class ProductA implements Product {
	@Override
	public void print() {
		//...输出逻辑;
	}
}

class ProductB implements Product {
	@Override
	public void print() {
		//...输出逻辑;
	}
}
typescript 复制代码
// 简单工厂
public class SimpleFactory {
	public Product getInstance(int type) {
		if(1 == type){
			return new ProductA();
		} else if(2 == brand){
			return new ProductB();
		}
		return null;
	}

	public static void main(String[] args) {
		SimpleFactory f = new SimpleFactory();
		Product p = f.getInstance(1);
		//...
	}
}

简单工厂通过在创建方法中传入创建参数,根据创建参数来创建具体 Product 对象。

很显然,随着 Product 子类逐渐增多,每增加一个 Product 子类,就要修改一次 Factory 的代码,不符合"开闭原则"。

工厂方法

为了应对"Product 子类逐渐增多"带来的代码膨胀问题,工厂方法则是使用继承的方法,为每个 Product 子类创建对应的 Factory 子类,每个 Factory 子类创建对应的 Product 子类对象。

UML 和示例代码如下:

图 2. 工厂方法的 UML

工厂方法的示例代码:

java 复制代码
public interface Factory {
    Product getInstance();
}

public class ProductAFactory implements Factory {
    @Override
    public Product getInstance(){
        return new ProductA();
    }
}

public class ProductBFactory implements Factory {
    @Override
    public Product getInstance(){
        return new ProductB();
    }
}

和"简单工厂"相比,即使 Product 的子类不断增多,也不需要去修改某个 Factory 类的代码,只需要新建一个对应 Factory 实现类来扩展即可,符合了"开闭原则"。

工厂方法存在的问题在于:随着 Product 子类的增多,Factory 子类也必然增多,这是不可避免的,需要使用其他方法来解决代码的维护问题。

抽象工厂

针对"工厂方法"存在的问题,如果存在多种 Product,即产品族,不同产品之间存在组合关系,那么多个产品的一种组合关系,可以使用一个 Factory 来创建,避免实现过多的 Factory 类。

UML 和代码示例如下:

图 3. 抽象工厂 UML

示例代码:

java 复制代码
public interface ProductA {
   void print();
}
public class ProductA1 implements ProductA {
    @Override
    public void print() {
        
    }
}
public class ProductA2 implements ProductA {
    @Override
    public void print() {
     
    }
}

public interface ProductB {
   void play();
}
public class ProductB1 implements ProductB {
    @Override
    public void play() {
    
    }
}
public class ProductB2 implements ProductB {
    @Override
    public void play() {

    }
}
java 复制代码
public interface IFactory {
    ProductA createProductA();
	ProductB createProductB();
}

public class Factory1 implements IFactory {
   
      @Override
      public ProductA createProductA(){
           return new ProductA1();
      }
	  @Override
      public ProductB createProductB(){
		  return new ProductB1();
      }
}

public class Factory2 implements IFactory {
      @Override
      public ProductA createProductA(){
           return new ProductA2();
      }
	  @Override
      public ProductB createProductB(){
		  return new ProductB2();
      }
}

和"工厂方法"相比,抽象工厂可以组合多种 Product,从而减少 Factory 类的个数,降低了代码的复杂度和维护难度。

适用场景

工厂方法的适用场景:

  • 当 client 不知道它要创建的对象所属的具体类时
  • 当 client 希望由 factory 的子类来创建自己所需的对象时

工厂方法不再将特定应用有关的类固化在代码中,代码仅需处理 Product 接口,为将来的扩展提供了方便。

抽象工厂的适用场景:

  • 一个系统要独立于它的产品的创建、组合和表示时
  • 一个系统要由多个产品族中一个来配置时
  • 当提供一个产品类库,只想显示他们的接口而不是实现时

抽象工厂分离了 client 和类的实现过程,并且易于改变产品族,只需要修改 Factory 接口的创建方式和组合方式,就可以生成一个新的产品族。

变和不变

设计模式的核心是分离问题中"变与不变"两个部分,在工厂模式中,不变的是"创建对象"这个要求,变化的是"创建什么对象"。

工厂模式分离了这两者:

  • 定义抽象类 Factory 提供"创建对象"方法
  • 由 Factory 子类来实现"创建具体的对象"

DDD 中的工厂

在 DDD 中也有使用到"工厂",不过和设计模式中的工厂相比,在 DDD 中,工厂只是用来创建领域模型中的对象,不承担其他的职责,是一个"职责单一"的对象,只是聚合根的行为之一。

如果是简单的创建过程,可以使用聚合根的静态方法 create 等,并非必须使用"工厂"。

小结

工厂模式中的三种模式,适用不同复杂度的场景,并且复杂度是逐渐上升的:

1、 在 product 分类很少时,可以通过简单工厂,传入参数来创建具体的对象。

2、随着 product 分类逐渐增加,每次都要修改简单工厂的方法,复杂度逐渐增加,从而改用"工厂方法",为每个 product 类设计一个 factory method,避免修改代码。

3、随着 product 分类进一步增加,并且多个 product 相互存在组合关系,复杂度进一步上升,所以由一个 factory method 负责组合多个 product 具体类,返回最终的复合对象。

可以看出,工厂模式是为了解决"由于代码膨胀带来创建对象越来越难维护"的问题。

设计模式最佳套路5 ------ 愉快地使用工厂方法模式中,对抽象工厂做了进一步抽象,来降低重复代码,降低代码的维护难度,并使用 spring 框架和泛型的特性,分别实现了"继承、组合"两种设计。

相关推荐
不懂9029 分钟前
Spring Boot集成Jetty、Tomcat或Undertow及支持HTTP/2协议
spring boot·后端·http·https
千里码!1 小时前
java23种设计模式-模板方法模式
java·设计模式·模板方法模式
攻城狮7号2 小时前
【第八节】C++设计模式(结构型模式)-Decorator(装饰器)模式
c++·设计模式·装饰器模式
总是学不会.2 小时前
从“记住我”到 Web 认证:Cookie、JWT 和 Session 的故事
java·前端·后端·开发
Neozsvc2 小时前
飞书工单审批对接腾讯电子签:开启合同流程自动化新时代
运维·人工智能·后端·自动化·飞书
℡52Hz★3 小时前
利用node.js搭配express框架写后端接口(一)
后端·node.js·express
IT闫3 小时前
【SpringBoot】——如何在Spring Boot中使用ThreadLocal来存储和获取用户详情信息以及实体类参数验证
java·spring boot·后端
北城望戈3 小时前
工作中遇到的设计模式整理
java·设计模式
编程诗人华仔3 小时前
若依框架实际国际化前后端统一解决方案
java·vue.js·spring boot·后端·elementui
LuckyLay4 小时前
Golang学习笔记_40——模版方法模式
笔记·学习·设计模式·golang·模板方法模式