GeekIBLi

设计模式之美-工厂模式

2021-08-07

1. 简单工厂模式

简单工厂模式是指有一个工厂对象决定创建出哪一个产品类的实例;属于创建型模式,但它不属于GOF 23种设计模式。

UML类图

组成要素
1、一个抽象产品类
2、具体产品类
3、一个工厂

肥宅喜爱的各种快乐水(产品接口)

1
2
3
public interface Kls {
String name();
}

肥宅快乐水-可乐(具体产品)

1
2
3
4
5
6
public class Coke implements Kls {
@Override
public String name() {
return "肥宅快乐水-可乐";
}
}

快乐水-雪碧(具体产品)

1
2
3
4
5
6
public class Sprite implements Kls {
@Override
public String name() {
return "快乐水-雪碧";
}
}

快乐水生产工厂(工厂类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class KlsFactory {
public static Kls getFzs(String type) throws Exception {
Kls fzs = null;
if ("coke".equalsIgnoreCase(type)) {
fzs = new Coke();
} else if ("sprite".equalsIgnoreCase(type)) {
fzs = new Sprite();
}
if (Objects.isNull(fzs)) {
throw new RuntimeException("没找到快乐水~");
}
return fzs;
}
}

客户端使用

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Fz {
@Test
public void drink() throws Exception {
// 制造可乐
Kls coke = KlsFactory.getFzs("coke");
System.out.println("肥宅开始喝:" + coke.name());

// 制造雪碧
Kls sprite = KlsFactory.getFzs("sprite");
System.out.println("肥宅开始喝:" + sprite.name());
}
}

1.1 优点

  • 对客户端隐藏的具体的实现,客户端只需要知道要创建什么对象即可,不用关心对象是怎么创建的
  • 解耦,客户端不需要通过new来创建对象,如果后期产品类需要修改,则只需要修改工厂类即可,不用整个项目遍地去寻找哪里有new

1.2 缺点

  • 由一个专门的工厂负责生产,如果业务变得复杂,这个类将变得十分臃肿
  • 工厂类生产什么产品都是写死在工厂中的,如果增加新的产品,还要修改工厂类的生产逻辑

2. 工厂方法模式

工厂方法模式(Factory Method)是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。

组成要素
1、一个抽象产品类
2、多个具体产品类
3、一个抽象工厂
4、多个具体工厂 - 每一个具体产品对应一个具体工厂
5、符合 - OCP开放封闭原则

接着上面快乐水的例子。将 快乐水工厂 (KlsFactory) 抽象出共有方法,再分别实现具体的快乐水生产工厂。

快乐水总工厂

1
2
3
4
5
6
7
8
public interface Factory {
/**
* 制造快乐水
*
* @return Kls
*/
Kls create();
}

可乐工厂

1
2
3
4
5
6
public class CokeFactory implements Factory {
@Override
public Kls create() {
return new Coke();
}
}

雪碧工厂

1
2
3
4
5
6
public class SpriteFactory implements Factory {
@Override
public Kls create() {
return new Sprite();
}
}

肥宅(客户端)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Fz {
@Test
public void drink() throws Exception {
// 制造可乐
CokeFactory cokeFactory = new CokeFactory();
Kls coke = cokeFactory.create();
System.out.println("肥宅开始喝:" + coke.name());

// 制造雪碧
SpriteFactory spriteFactory = new SpriteFactory();
Kls sprite = spriteFactory.create();
System.out.println("肥宅开始喝:" + sprite.name());
}
}

扩展芬达快乐水

1
2
3
4
5
6
public class Fanta implements Kls {
@Override
public String name() {
return "快乐水-芬达";
}
}

复制代码芬达工厂

1
2
3
4
5
6
public class FantaFactory implements Factory {
@Override
public Kls create() {
return new Fanta();
}
}

肥宅使用添加

1
2
3
FantaFactory fantaFactory = new FantaFactory();
Kls fanta = fantaFactory.create();
System.out.println("肥宅开始喝:" + fanta.name());

2.1 优点

1、降低了代码耦合度,对象的生成交给子类去完成(这里的耦合度是相对于简单工厂模式的工厂类比较的)
2、降低了代码耦合度,对象的生成交给子类去完成

2.2 缺点

1、增加了代码量,每个具体产品都需要一个具体工厂(在具体的业务中可能会产生大量的重复代码)
2、当增加抽象产品 也就是添加一个其他产品族 需要修改工厂 违背OCP

3. 抽象工厂模式

组成要素
1、多个抽象产品类
2、具体产品类
3、抽象工厂类 - 声明(一组)返回抽象产品的方法
4、具体工厂类 - 生成(一组)具体产品

一个抽象产品类和两个具体的产品(可乐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class Coke {
public abstract void doCreate();
}

public class LocalCoke extends Coke{
@Override
public void doCreate() {
System.err.println("生产本土可乐");
}
}

public class ForeignCoke extends Coke{
@Override
public void doCreate() {
System.out.println("生产外国可乐");
}
}

一个抽象产品类和两个具体的产品(雪碧)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class Sprite {
public abstract void doCreate();
}

public class LocalSprite extends Sprite{
@Override
public void doCreate() {
System.out.println("生产本地雪碧");
}
}

public class ForeignSprite extends Sprite{
@Override
public void doCreate() {
System.err.println("生产外国雪碧");
}
}

一个抽象工厂和两个具体的工厂

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
49
50
51
52
53
54
55
56
57
58
59
60
61
public interface IAbstractFactory {

/**
* 成产可乐
*
* @return 声明(一组)返回抽象产品的方法
*/
Coke createCoke();

/**
* 生产雪碧
*
* @return 声明(一组)返回抽象产品的方法
*/
Sprite createSprite();
}

public class LocalFactory implements IAbstractFactory{

/**
* 成产可乐
*
* @return 生成(一组)具体产品
*/
@Override
public LocalCoke createCoke() {
return new LocalCoke();
}

/**
* 生产雪碧
*
* @return 生成(一组)具体产品
*/
@Override
public LocalSprite createSprite() {
return new LocalSprite();
}
}

public class ForeignFactory implements IAbstractFactory{
/**
* 成产可乐
*
* @return 生成(一组)具体产品
*/
@Override
public ForeignCoke createCoke() {
return new ForeignCoke();
}

/**
* 生产雪碧
*
* @return 生成(一组)具体产品
*/
@Override
public ForeignSprite createSprite() {
return new ForeignSprite();
}
}

3.1 抽象工厂的使用

1
2
3
4
5
6
7
8
9
10
11
public class FactoryTest {
public static void main(String[] args) {
LocalFactory localFactory = new LocalFactory();
LocalCoke localCoke = localFactory.createCoke();
localCoke.doCreate();

ForeignFactory foreignFactory = new ForeignFactory();
ForeignSprite foreignSprite = foreignFactory.createSprite();
foreignSprite.doCreate();
}
}

3.2 优点

1、代码解耦
2、很好的满足OCP开放封闭原则
3、抽象工厂模式中我们可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类 对于复杂对象的生产相当灵活易扩展
(相对于工厂方法模式的优化)

3.3 缺点

1、扩展产品是需要修改所有工厂,违反违反OCP原则
2、整体实现也比较复杂

参考文章

还有多少个十年 继续做热血少年