工厂模式

工厂模式是最常用的一类创建型设计模式,之前一直以为工厂模式只是23中设计模式中的一种,重新了解才知道这个模式还要细分为三种工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式。这三个工厂模式各有特色,难度也逐个加大,在软件开发中它们都得到了广泛的应用。

有意思的是,我以为对于这三种工厂模式的理解应该都是一样,但是阅读了N篇关于工厂模式的博文后,发现他们好多关于这三个工厂模式的定义都不一致,有些人定义的抽象工厂模式却是另一个人定义的工厂方法模式,让人很是纠结啊😂。

从众多的博文中我推荐刘伟技术博客,虽然博客比较早,但是关于设计模式的博客内容真的很详细,值得学习!而且他的每一篇博客后基本上都留有一个练习题,我决定以他的练习题为入口来学习学习设计模式,源码可在我的github上获取。

简单工厂模式(Simple Factory Pattern)

定义

定义一个工厂类,它可以根据参数的不同返回不同的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。

简单说,就在当你需要什么,传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节。

实例

使用简单工厂模式设计一个可以创建不同几何形状(如圆形、方形和三角形等)的绘图工具,每个几何图形都具有绘制draw()和擦除erase()两个方法。

结构图

根据简单工厂模式设计的结构图如下(推荐一款在线绘制UML的软件ProcessOn)

代码

完整代码如下

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
39
40
41
42
43
44
45
46
47
48
//图形接口
public interface Shape {
void draw();
void erase();
}

//圆形
public class RoundShape implements Shape {

@Override
public void draw() {
System.out.println("画了一个圆形!");
}

@Override
public void erase() {
System.out.println("擦除一个圆形!");
}
}

//三角形
public class TriangleShape implements Shape {
@Override
public void draw() {
System.out.println("画了一个三角形!");
}

@Override
public void erase() {
System.out.println("擦除一个三角形!");
}
}


//工厂类
public class ShapeFactory {

public static Shape getShape(String type) {
Shape shape = null;

if (type.equalsIgnoreCase("ROUND")) {
shape = new RoundShape();
} else if (type.equalsIgnoreCase("TRIANGLE")) {
shape = new TriangleShape();
}
return shape;
}
}

客户端测试代码如下

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

public static void main(String[] args) {
Shape shape;

shape = ShapeFactory.getShape("round");
shape.draw();
shape.erase();

shape = ShapeFactory.getShape("triangle");
shape.draw();
shape.erase();
}
}

运行结果如下

1
2
3
4
画了一个圆形!
擦除一个圆形!
画了一个三角形!
擦除一个三角形!

小结

简单工厂模式中工厂类的职责太重,一旦不能工作将影响整个系统,且如果添加过多的产品,工厂类中的逻辑就过于复杂。所以简单工厂模式适用于逻辑简单,且需要创建对象较少的情况。

工厂方法模式(Factory Method Pattern)

定义

定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。

工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,针对不同的产品提供不同的工厂。

实例

使用工厂方法模式设计一个程序来读取各种不同类型的图片格式,针对每一种图片格式都设计一个图片读取器,如GIF图片读取器用于读取GIF格式的图片、JPG图片读取器用于读取JPG格式的图片。需充分考虑系统的灵活性和可扩展性。

结构图

根据工厂方法模式创建的结构图如下

从结构图可以知道,我们针对每一种读取器都创建了一个对应的工厂类,而所有的工厂类都继承自一个抽象工厂方法,在Client中我们只需定义抽象工厂类,具体实现可自己选择。

代码

完整代码如下

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
39
40
41
42
43
44
45
46
47
//读取器的接口
public interface Reader {
void readPic();
}

//工厂类接口:抽象方法
public interface ReaderFactory {

Reader createReader();
}



//定义两种读取器
public class JpgReader implements Reader {

@Override
public void readPic() {
System.out.println("读取JPG格式图片!");
}
}

public class GifReader implements Reader {
@Override
public void readPic() {
System.out.println("读取gif格式图片");
}
}


//创建对应的工厂方法
public class JpgReaderFactory implements ReaderFactory {
@Override
public Reader createReader() {
//初始化读取器的细节
return new JpgReader();
}
}


public class GifReaderFactory implements ReaderFactory {
@Override
public Reader createReader() {
//初始化读取器的细节
return new GifReader();
}
}

客户端测试代码如下

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

public static void main(String[] args) {
ReaderFactory readerFactory;
Reader reader;
readerFactory = new GifReaderFactory();
reader = readerFactory.createReader();
reader.readPic();


readerFactory = new JpgReaderFactory();
reader = readerFactory.createReader();
reader.readPic();
}
}

测试运行结果如下

1
2
读取gif格式图片
读取JPG格式图片!

小结

工厂方法模式是对简单工厂模式的升级应用,它解决了简单工厂模式中工厂类职责繁重的问题,且更符合设计模式的开闭原则,增加新的产品不需要修改原先的代码,只需要增加对应的产品实例及工厂实例即可,同时这也是工厂方法模式的一个缺点,添加新产品,系统中的类就需要成对的增加,一定程度上增加了系统的复杂度。

在工厂方法模式中,客户并不需要知道具体产品类创建的细节,只需要关心产品对应的工厂,根据工厂创建自己需要的产品就行了。

抽象工厂模式(Abstract Factory Pattern)

定义

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。

光看定义相信大部分小伙伴跟我一样是一脸懵逼,与简单工厂或工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。

如上图所示,我们之前学习的简单工厂模式及工厂方法模式解决的都只是一种产品的创建过程,而在现实中,一个工厂往往不仅仅生产一种产品,例如惠普的工厂可能同时生产打印机、洗衣机、冰箱等产品,这里我们就需要对于一个产品族创建一个抽象工厂,在这个在具体的某一个产品族的工厂中创建具体的产品。

结构图如下

下面还是根据一个实例来理解抽象工厂模式

实例

Sunny软件公司欲推出一款新的手机游戏软件,该软件能够支持Symbian、Android和Windows Mobile等多个智能手机操作系统平台,针对不同的手机操作系统,该游戏软件提供了不同的游戏操作控制(OperationController)类和游戏界面控制(InterfaceController)类,并提供相应的工厂类来封装这些类的初始化过程。软件要求具有较好的扩展性以支持新的操作系统平台,为了满足上述需求,试采用抽象工厂模式对其进行设计。

结构图

根据抽象工厂模式定义设计结构图如下

根据需求我们知道,当该游戏在不同的操作系统时,需要分别使用不同的操作控制器和界面控制器,所以我们针对不同操作系统设计一个抽象工厂,该抽象工厂中定义两个方法,分别生产操作控制器和界面控制器。然后生产不同系统的工厂类,在工厂类中生成对应的操作控制器和界面控制器。

代码

操作控制器代码

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
//操作控制器接口
public interface OperationController {
void controlOperation();
}


//生产三种操作控制器接口
public class SymbianOperationController implements OperationController {
@Override
public void controlOperation() {
System.out.println("使用Symbian系统操作控制");
}
}

public class AndroidOperationController implements OperationController {
@Override
public void controlOperation() {
System.out.println("使用Android系统操作控制");
}
}

public class WindowsOperationController implements OperationController {
@Override
public void controlOperation() {
System.out.println("使用Windows Mobile系统操作控制");
}
}

界面控制器代码

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
//界面控制器接口
public interface InterfaceController {

void controlInterface();
}


//分别生成三种界面控制器
public class SymbianInterfaceController implements InterfaceController {
@Override
public void controlInterface() {
System.out.println("使用Symbian系统游戏界面控制");
}
}

public class AndroidInterfaceController implements InterfaceController {
@Override
public void controlInterface() {
System.out.println("使用Android系统游戏界面控制");
}
}

public class WindowsInterfaceController implements InterfaceController {
@Override
public void controlInterface() {
System.out.println("使用Windows Mobile系统游戏界面控制");
}
}

抽象工厂代码

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
39
40
41
42
43
44
//抽象工厂接口
public interface SysFactory {

InterfaceController createInterface();

OperationController createOperation();
}

//生产三种不同系统的抽象工厂
public class SymbianSysFactory implements SysFactory {
@Override
public InterfaceController createInterface() {
return new SymbianInterfaceController();
}

@Override
public OperationController createOperation() {
return new SymbianOperationController();
}
}

public class AndroidSysFactory implements SysFactory {
@Override
public InterfaceController createInterface() {
return new AndroidInterfaceController();
}

@Override
public OperationController createOperation() {
return new AndroidOperationController();
}
}

public class WindowsSysFactory implements SysFactory {
@Override
public InterfaceController createInterface() {
return new WindowsInterfaceController();
}

@Override
public OperationController createOperation() {
return new WindowsOperationController();
}
}

客户端测试代码

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

public static void main(String[] args) {
SysFactory sysFactory;
InterfaceController interfaceController;
OperationController operationController;

sysFactory = new SymbianSysFactory();
interfaceController = sysFactory.createInterface();
operationController = sysFactory.createOperation();

interfaceController.controlInterface();
operationController.controlOperation();
}
}

测试结果如下

1
2
使用Symbian系统游戏界面控制
使用Symbian系统操作控制

当我们需要切换不同的操作系统时,只需要修改

1
sysFactory = new AndroidSysFactory();

结果就是

1
2
使用Android系统游戏界面控制
使用Android系统操作控制

小结

抽象工厂模式是对工厂方法模式的进一步升级使用,使得工厂方式更符合实际开发需求,在实际开发中往往需要一个产品族来完成工作。抽象工厂模式在新增产品族时很方便,无需修改已有系统,符合“开闭原则”。但抽象工厂模式最大的缺点是对于新增产品很麻烦,需要对原有系统进行较大的修改,不符合“开闭原则”。所以在实际开发中,我们需要衡量具体的需求,选择对应的设计模式,对于需要客户端使用同一产品族的需求时,可以选择抽象工厂模式进行设计。


源码:https://github.com/lichenming0516/DesignPattern

坚持原创技术分享,您的支持将鼓励我继续创作!