设计模式之工厂模式

设计模式之工厂模式

简单工厂模式

初识需要变化的方面

假如你有一个比萨店,身为比萨店的主人,你的代码可能会这么写:

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
/**
* 普通比萨
* @author Mr.zxb
* @date 2020-08-14 21:39:59
*/
public class Pizza {

/**
* 制作比萨
* @return
*/
private Pizza orderPizza() {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

/**
* 打包比萨
*/
private void box() {
System.out.println("Packed Pizza");
}

/**
* 切比萨
*/
private void cut() {
System.out.println("Cut Pizza");
}

/**
* 烘烤比萨
*/
private void bake() {
System.out.println("Bake Pizza");
}

/**
* 准备比萨
*/
private void prepare() {
System.out.println("Prepare Pizza");
}
}

假如你需要更多的比萨类型

所以需要增加一些代码,来决定适合的比萨类型,然后再制作比萨:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private Pizza orderPizza(String type) {
Pizza pizza = null;
// 根据不同的类型,创建不同的实例对象
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("greek".equals(type)) {
pizza = new GreekPizza();
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

如果需要增加更多的类型比萨

此时,如果我们需要增加更多可口的比萨呢,就需要在代码中重新增加类型,随着时间的变化,类型会不断的改变,这无疑是个灾难。

这个时候,该是用封装的时候了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private Pizza orderPizza(String type) {
Pizza pizza = null;
// 根据不同的类型,创建不同的实例对象
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("greek".equals(type)) {
pizza = new GreekPizza();
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ClamPizza();
} else if ("veggie".equals(type)) {
pizza = new VeggiePizza();
}
// ... 省略制作过程
return pizza;
}

建立一个简单比萨工厂

先从工厂本身开始,我们要为所有比萨封装创建对象的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 简单的比萨工厂
* @author Mr.zxb
* @date 2020-08-14 22:05:11
*/
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
// 根据不同的类型,创建不同的实例对象
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("greek".equals(type)) {
pizza = new GreekPizza();
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ClamPizza();
} else if ("veggie".equals(type)) {
pizza = new VeggiePizza();
}
return pizza;
}
}

设计比萨店类

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
/**
* 比萨店
* @author Mr.zxb
* @date 2020-08-14 21:39:59
*/
public class PizzaStore {
private final SimplePizzaFactory simplePizzaFactory;
public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
this.simplePizzaFactory = simplePizzaFactory;
}

/**
* 制作比萨
* @return
*/
private Pizza orderPizza(String type) {
Pizza pizza = simplePizzaFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

// 其他方法
}

让我们来定义简单工厂

简单工厂其实不是一个设计模式,反而比较像是一种编程习惯。

简单工厂模式类图

工厂方法模式

加盟比萨店

因为我们的比萨店经营有成,现在很多人想加盟我们的比萨店。但是不同区域是有差异的, 比如:纽约、芝加哥、加州等各地的加盟店之间口味也有差异。

改造我们的比萨店

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 我们的比萨店
* @author Mr.zxb
* @date 2020-08-14 22:20:19
*/
public abstract class PizzaStore {
// 订购比萨
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

return pizza;
}

// 定义抽象的制作比萨方法
protected abstract Pizza createPizza(String type);
}

现在已经有一个PizzaStore作为超类,让每个区域加盟比萨店(NewYorkPizzaStore、ChicagoPizzaStore)都继承这个PizzaStore,每个子类各自决定如何制造比萨。

让我们开一家纽约风味的比萨店吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 纽约风味的比萨店
* @author Mr.zxb
* @date 2020-08-14 22:18:48
*/
public class NewYorkPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
// 根据不同的类型,创建不同的实例对象
if ("cheese".equals(type)) {
pizza = new NewYorkStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new NewYorkStylePepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new NewYorkStyleClamPizza();
} else if ("veggie".equals(type)) {
pizza = new NewYorkStyleVeggiePizza();
}
return pizza;
}
}

让我们再开一家芝加哥风味的比萨店吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 芝加哥风味的比萨店
* @author Mr.zxb
* @date 2020-08-14 22:18:48
*/
public class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
// 根据不同的类型,创建不同的实例对象
if ("cheese".equals(type)) {
pizza = new ChicagoStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new ChicagoStylePepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ChicagoStyleClamPizza();
} else if ("veggie".equals(type)) {
pizza = new ChicagoStyleVeggiePizza();
}
return pizza;
}
}

改造我们的比萨吧

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
/**
* 普通比萨
* @author Mr.zxb
* @date 2020-08-14 21:39:59
*/
public class Pizza {

protected String name;
protected String dough;
protected String sauce;
protected ArrayList<String> toppings = new ArrayList<>();

/**
* 打包比萨
*/
public void box() {
System.out.println("Place pizza in official PizzaStore box");
}

/**
* 切比萨
*/
public void cut() {
System.out.println("Cut the pizza into diagonal slices");
}

/**
* 烘烤比萨
*/
public void bake() {
System.out.println("Bake for 25 minutes at 350");
}

/**
* 准备比萨
*/
public void prepare() {
System.out.println("Prepare " + name +" Pizza");
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings: ");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" " + toppings.get(i));
}
}

public String getName() {
return name;
}
}

设计纽约风味的比萨

1
2
3
4
5
6
7
8
9
// 省略其他纽约风味的口味比萨
public class NewYorkStyleCheesePizza extends Pizza {
public NewYorkStyleCheesePizza() {
super.name = "New York Style Sauce and Cheese Pizza";
super.dough = "Thin Crust Dough";
super.sauce = "Marinara Sauce";
super.toppings.add("Grated Reggiano Cheese");
}
}

设计芝加哥风味的比萨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 省略其他芝加哥风味的口味比萨
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza() {
super.name = "Chicago Style Deep Dish Cheese Pizza";
super.dough = "Extra Thick Crust Dough";
super.sauce = "Plum Tomato Sauce";
super.toppings.add("Shredded Mozzarella Cheese");
}

// 将芝加哥风味比萨切成正方形
@Override
public void cut() {
System.out.println("Cutting the pizza into square slices");
}
}

久等了,让我们来制作比萨吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PizzaTestDrive {
public static void main(String[] args) {
// 创建纽约风味的比萨店
PizzaStore nyStore = new NewYorkPizzaStore();
// 创建芝加哥风味的比萨店
PizzaStore chicagoStore = new ChicagoPizzaStore();

Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");

pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}

认识工厂方法模式的时刻到了

所有工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类来决定该创建的对象是什么,来达到将对象创建的过程封装的目的。

工厂方法模式类图

定义工厂方法模式

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

### 抽象工厂模式

#### 设计原则

要依赖对象,不要依赖具体类

#### 依赖倒置原则

下面的指导方针,能帮助我们避免在OO设计中违反依赖倒置原则:

- 变量不可以持有具体类的引用
- 不要让类派生自具体类
- 不要覆盖基类中已实现的方法

#### 重新建造原料工厂

```java
/**
* 建造原料工厂
* @author Mr.zxb
* @date 2020-08-15 10:05:27
*/
public interface PizzaIngredientFactory {
/**
* 创建面团
* @return
*/
Dough createDough();

/**
* 创建酱汁
* @return
*/
Sauce createSauce();

/**
* 创建奶酪
* @return
*/
Cheese createCheese();

/**
*创建蔬菜
* @return
*/
Veggies[] createVeggies();

/**
*创建意大利辣香肠
* @return
*/
Peppernoi createPepperoni();

/**
* 创建蛤蜊
* @return
*/
Clams createClam();
}

创建纽约原料工厂

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
/**
* 纽约比萨配料厂
* @author Mr.zxb
* @date 2020-08-15 10:11:51
*/
public class NewYorkPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}

@Override
public Sauce createSauce() {
return new MarinaraSauce();
}

@Override
public Cheese createCheese() {
return new ReggianoCheese();
}

@Override
public Veggies[] createVeggies() {
return new Veggies[]{ new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
}

@Override
public Peppernoi createPepperoni() {
return new SlicedPeppernoi();
}

@Override
public Clams createClam() {
return new FreshClams();
}
}

重做比萨··· ···

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
import java.util.Arrays;

/**
* 比萨抽象类
* @author Mr.zxb
* @date 2020-08-15 10:28:24
*/
public abstract class Pizza {
protected String name;
protected Dough dough;
protected Sauce sauce;
protected Veggies[] veggies;
protected Cheese cheese;
protected Peppernoi peppernoi;
protected Clams clams;

abstract void prepare();

/**
* 打包比萨
*/
public void box() {
System.out.println("Place pizza in official PizzaStore box");
}

/**
* 切比萨
*/
public void cut() {
System.out.println("Cut the pizza into diagonal slices");
}

/**
* 烘烤比萨
*/
public void bake() {
System.out.println("Bake for 25 minutes at 350");
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

@Override
public String toString() {
return "Pizza{" +
"name='" + name + '\'' +
", dough=" + dough +
", sauce=" + sauce +
", veggies=" + Arrays.toString(veggies) +
", cheese=" + cheese +
", peppernoi=" + peppernoi +
", clams=" + clams +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 芝士披萨
* @author Mr.zxb
* @date 2020-08-15 10:31:14
*/
public class CheesePizza extends Pizza {

private final PizzaIngredientFactory ingredientFactory;

public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}

@Override
void prepare() {
System.out.println("Preparing " + name);
super.dough = ingredientFactory.createDough();
super.sauce = ingredientFactory.createSauce();
super.cheese = ingredientFactory.createCheese();
super.veggies = ingredientFactory.createVeggies();
super.clams = ingredientFactory.createClam();
super.peppernoi = ingredientFactory.createPepperoni();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 蛤蜊比萨
* @author Mr.zxb
* @date 2020-08-15 10:31:14
*/
public class ClamPizza extends Pizza {

private final PizzaIngredientFactory ingredientFactory;

public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}

@Override
void prepare() {
System.out.println("Preparing " + name);
super.dough = ingredientFactory.createDough();
super.sauce = ingredientFactory.createSauce();
super.cheese = ingredientFactory.createCheese();
}
}

再回到比萨店

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 比萨店
*
* @author Mr.zxb
* @date 2020-08-14 22:20:19
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

protected abstract Pizza createPizza(String type);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 纽约风味比萨店
* @author Mr.zxb
* @date 2020-08-15 10:33:49
*/
public class NewYorkPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
PizzaIngredientFactory ingredientFactory = new NewYorkPizzaIngredientFactory();
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
} else if ("clam".equals(type)) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
}
// ... 其他口味方法
return pizza;
}
}

让我们来制作新的比萨吧

1
2
3
4
5
6
7
8
9
public class PizzaStoreTest {
public static void main(String[] args) {
// 创建纽约风味比萨店
PizzaStore nyPizzaStore = new NewYorkPizzaStore();
// 制作芝士比萨
Pizza pizza = nyPizzaStore.orderPizza("cheese");
System.out.println(pizza);
}
}

定义抽象工厂模式

抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道实际产出的具体产品是什么。这样一来,客户就从具体的产品中解耦。

抽象工厂模式类图

总结

  • 所有的工厂都是用来封装对象的创建
  • 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦
  • 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中
  • 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合
  • 工厂方法允许类将实例化延迟到子类进行
  • 抽象工厂创建相关的对象家族,而不需要依赖它们的具体类
  • 依赖倒置原则,指导我们避免依赖具体类型,而尽量依赖抽象
  • 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类的编程