设计模式-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 框架和泛型的特性,分别实现了"继承、组合"两种设计。

相关推荐
海绵波波1072 小时前
flask后端开发(10):问答平台项目结构搭建
后端·python·flask
ke_wu3 小时前
结构型设计模式
开发语言·设计模式·组合模式·简单工厂模式·工厂方法模式·抽象工厂模式·装饰器模式
小马爱打代码3 小时前
设计模式详解(建造者模式)
java·设计模式·建造者模式
小王爱吃月亮糖3 小时前
C++的23种设计模式
开发语言·c++·qt·算法·设计模式·ecmascript
_im.m.z3 小时前
【设计模式学习笔记】1. 设计模式概述
笔记·学习·设计模式
网络风云3 小时前
【魅力golang】之-反射
开发语言·后端·golang
Q_19284999063 小时前
基于Spring Boot的电影售票系统
java·spring boot·后端
运维&陈同学5 小时前
【Kibana01】企业级日志分析系统ELK之Kibana的安装与介绍
运维·后端·elk·elasticsearch·云原生·自动化·kibana·日志收集
Javatutouhouduan7 小时前
如何系统全面地自学Java语言?
java·后端·程序员·编程·架构师·自学·java八股文
后端转全栈_小伵7 小时前
MySQL外键类型与应用场景总结:优缺点一目了然
数据库·后端·sql·mysql·学习方法