设计模式(2)-工厂模式图文介绍

工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

模拟需求①

假设现有一个口罩生产工厂,可以生产防霾口罩、医用一次性口罩、N95口罩
客户可以通过口罩直营店根据自己的需求下单购买口罩
使用代码实现这一流程

传统实现方式

根据给出的需求,结合面向对象思想,大概有以下几个类

  • BaseMask 抽象口罩类
  • HazeMask 防霾口罩类
  • MedicalMask 医用口罩类
  • N95Mask N95口罩类
  • MaskStore 直营店类
  • Client 客户类

简单类图如下:

实现代码

HazeMask、MedicalMask、N95Mask继承自BaseMask,分别实现prepare方法,并调用setName方法设置name属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public abstract class BaseMask {
protected String name;
public abstract void prepare();
public void processing(){
System.out.println(name+"开始加工...");
}
public void bale(){
System.out.println(name+"打包完成...");
}
public void setName(String name) {
this.name = name;
}
}

MaskStore类,实现了口罩直营店根据用户需求进行下单的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MaskStore {
public void order() {
BaseMask mask = null;
int maskType;
do {
maskType = getType();
if (1 == maskType){
mask = new HazeMask();
}else if (2 == maskType){
mask = new MedicalMask();
}else if (3 == maskType){
mask = new N95Mask();
}else {
System.out.println("不支持的产品类型");
break;
}
mask.prepare();
mask.processing();
mask.bale();
} while (true);
}

/**接收用户要下单的产品类型
* 1:防霾口罩
* 2:医用口罩
* 3:n95口罩
* */
private int getType() {
try {
BufferedReader typeReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入需要下单的类型: ");
return Integer.parseInt(typeReader.readLine());
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}

Client的实现就相对简单,模拟用户下单操作,直接调用直营店暴露的下单order方法

优缺点分析

根据场景需求我们有了如上的代码方案,其中涉及到的类和方法都比较好理解,核心主要是通过用户需要下单的type来进行产品的创建,但优缺点需要细细捋一捋

优点:思路清晰,便于理解
缺点:违反开闭原则,也就是扩展性差,如果添加一个新的口罩类型,涉及到的修改点过多

举个栗子:
如果这时候添加一个新的口罩类型,那所有的口罩直营店类中的代码都需要同步修改

这时候有一种解决方案:将根据类型创建产品的方法单独封装起来,当有新产品加入时,只需要修改单独封装过的这部分代码,而调用方可以做到无感知接入,这种方式也叫做简单工厂模式。但他并不属于23种设计模式,简单工厂仅仅指一种创建类的解决方案

简单工厂模式

相对于传统方案中多出一个简单工厂类SimpleMaskFactory,同时对MaskStore进行了重构,简单类图如下:

代码实现

与传统方案不同的是,之前的口罩产品创建是在MaskStore中,使用简单工厂模式后,将创建口罩产品的工作封装到了SimpleMaskFactory中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class SimpleMaskFactory {

public BaseMask createMask(int maskType) {
BaseMask mask = null;
if (1 == maskType) {
mask = new HazeMask();
} else if (2 == maskType) {
mask = new MedicalMask();
} else if (3 == maskType) {
mask = new N95Mask();
}
return mask;
}
}

MaskStore只需要持有工厂类和需要下单的产品类型,发起下单操作即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MaskStore {
private SimpleMaskFactory factory;

public MaskStore(SimpleMaskFactory factory) {
this.factory = factory;
}

public void order() {
BaseMask mask = null;
int maskType;
do {
maskType = getType();
mask = factory.createMask(maskType);
if (!Objects.isNull(mask)){
mask.prepare();
mask.processing();
mask.bale();
}else {
System.out.println("不支持的产品类型...");
break;
}
} while (true);
}

private int getType() {
try {
BufferedReader typeReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("----------------");
System.out.println("输入需要下单的类型: ");
return Integer.parseInt(typeReader.readLine());
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}

Client客户端的调用也更加方便

1
2
3
4
5
public class Client {
public static void main(String[] args) {
new MaskStore(new SimpleMaskFactory()).order();
}
}

模拟需求②

假设现有多个口罩生产工厂,大致分为杭州制造和上海制造,可以生产防霾口罩、医用一次性口罩,
客户可以通过自己的需求下单购买某个地址制造的某一种口罩
使用代码实现这一流程

此时的需求不仅有地域区分,同时还有种类区分,这种场景该如何处理呢?

  • 方案1
    • 使用简单工厂模式,根据地域创建不同的工厂类,通过不同的工厂类来进行不同的产品创建
    • 扩展性差,可维护性差
  • 方案2
    • 使用工厂方法模式,将创建产品的方法抽象化,创建对象的操作交给子类自己来完成,即将对象实例化推迟到子类

工厂方法模式

与简单工厂模式所不同,工厂方法模式将定义一个创建对象的抽象方法,根据实际需求整理到所涉及的类有

  • BaseMask 抽象的口罩类
  • HangzhouHazeMask 杭州制造-防霾口罩
  • HangzhouMedicalMask 杭州制造-医用口罩
  • ShanghaiHazeMask 上海制造-防霾口罩
  • ShanghaiMedicalMask 上海制造-医用口罩
  • BaseMaskFactory 抽象口罩工厂类,定义了一个创建对象的抽象方法,将对象创建延缓到子类进行
  • HangzhouMaskFactory 杭州制造工厂类
  • ShanghaiMaskFactory 上海制造工厂类
  • Client 客户类

简单的类图如下:

代码实现

HangzhouHazeMask、HangzhouMedicalMask、ShanghaiHazeMask、ShanghaiMedicalMask继承自BaseMask,分别实现prepare方法,并调用setName方法设置name属性

HangzhouMaskFactory、ShanghaiMaskFactory继承自BaseMaskFactory类,重写了抽象方法createMask方法实现自己的对象创建逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

public abstract class BaseMaskFactory {
//抽象方法,子类自己实现对象的创建
abstract BaseMask createMask(int maskType);

public BaseMaskFactory() {
BaseMask mask = null;
int maskType;
do {
//1:防霾口罩 2:医用口罩
maskType = getType();
mask = createMask(maskType);
if (!Objects.isNull(mask)) {
mask.prepare();
mask.processing();
mask.bale();
}else {
System.out.println("不支持的产品类型...");
break;
}
} while (true);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

public class HangzhouMaskFactory extends BaseMaskFactory{
@Override
BaseMask createMask(int maskType) {
BaseMask mask = null;
if (1==maskType){
mask = new HangzhouHazeMask();
}else if (2==maskType){
mask = new HangzhouMedicalMask();
}
return mask;
}
}

此时的客户调用,可以有选择性的指定某一地区来进行下单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {

public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请选择要购买的产品产地,1:杭州,2:上海");
int location = Integer.parseInt(scanner.nextLine());
if (1 == location) {
new HangzhouMaskFactory();
} else if (2 == location) {
new ShanghaiMaskFactory();
} else {
System.out.println("暂无该地区产品");
}
}
}

模拟需求③

假设现有两种产品要进行生产:口罩和酒精,并且有杭州和上海两个工厂都可以生产这两种产品
客户可以通过自己的需求下单购买某个地址制造的某一种产品
使用代码实现这一流程

这次的需求不同以往,产品类型出现了多种,即一个工厂可以生产多种不同类型的产品,这种涉及到多个产品簇,比较推荐使用抽象工厂模式

抽象工厂模式

抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,
且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式中一个工厂只生产一种产品,而在抽象工厂模式中,一个工厂生产多种产品,并且存在多个工厂

抽象工厂模式中有这两个概念

  • 产品等级:产品等级可以理解为同一类产品属于一个等级,比如防霾口罩、与医用外科口罩都属于口罩类,属于一个产品等级,但口罩和酒精明显不是一个产品等级
  • 产品族:同一个具体工厂所生产的位于不同产品等级的所有产品称作一个产品族。比如杭州工厂生产的杭州口罩和酒精就属于一个产品族

上面的需求用抽象工厂模式的思路得到的简单类图如下:

代码实现

其中HangzhouMask、ShanghaiMask都继承自BaseMask,HangzhouAlcohol、ShanghaiAlcohol继承自BaseAlcohol

通过定义抽象工厂接口AbstractMaskFactory,定义创建产品的方法,交由子类工厂进行实现。这里的产品创建方法可以覆盖到所有的产品等级

1
2
3
4
5

public interface AbstractFactory {
BaseMask createMask();
BaseAlcohol createAlcohol();
}
1
2
3
4
5
6
7
8
9
10
11
12
public class HangzhouFactory implements AbstractFactory{

@Override
public BaseMask createMask() {
return new HangzhouHazeMask();
}

@Override
public BaseAlcohol createAlcohol() {
return new HangzhouAlcohol();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

public class ShanghaiFactory implements AbstractFactory{

@Override
public BaseMask createMask() {
return new ShanghaiHazeMask();
}

@Override
public BaseAlcohol createAlcohol() {
return new ShanghaiAlcohol();
}
}

创建了工厂类后,客户可以通过某一工厂进行指定产品的下单操作,这些逻辑封装在了Store类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Store {
private AbstractFactory factory;

public Store(AbstractFactory factory) {
this.factory = factory;
}

public void orderMask() {
BaseMask mask = null;
mask = factory.createMask();
if (!Objects.isNull(mask)) {
mask.prepare();
mask.processing();
mask.bale();
} else {
System.out.println("不支持的产品类型...");
}
}

public void orderAlcohol() {
BaseAlcohol alcohol = null;
alcohol = factory.createAlcohol();
if (!Objects.isNull(alcohol)) {
alcohol.prepare();
alcohol.processing();
alcohol.bale();
} else {
System.out.println("不支持的产品类型...");
}
}
}

3种工厂模式的总结

本文一共提到了三种工厂模式,简单工厂模式、工厂方法模式、抽象工厂模式,也根据模拟场景对其进行了简单的说明

从上面的介绍中可以简单做下总结

  • 简单工厂模式
    • 实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责
    • 工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码
    • 而且产品较多时,工厂方法代码逻辑将会非常复杂
  • 工厂方法模式
    • 定义一个抽象的核心工厂类,并定义创建产品对象的接口,创建具体产品实例的工作延迟到其工厂子类去完成
    • 系统需要新增一个产品是,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类
    • 系统的扩展性变得很好,符合面向对象编程的开闭原则
  • 抽象工厂模式
    • 工厂模式的升级版,工厂方法模式中一个工厂负责生产一类产品,而抽象工厂模式中一个工厂可以生产多种产品
    • 扩展性更强,无论是增加工厂,还是增加产品,抽象工厂模式都比工厂方法模式更为便捷

关于工厂方法模式和抽象工厂模式的几点区别如下:

  • 工厂方法模式利用继承,抽象工厂模式利用组合
  • 工厂方法模式产生一个对象,抽象工厂模式产生一族对象
  • 工厂方法模式利用子类创造对象,抽象工厂模式利用接口的实现创造对象

常见的工厂模式的运用

  • JDK中Calendar的getlnstance方法
  • JDBC中的Connection对象的获取
  • Spring中IOC容器创建管理bean对象
  • 反射中Class对象的newlnstance方法
---------- 😏本文结束  感谢您的阅读😏 ----------
评论