《Head First 设计模式》笔记4

工厂模式(Factory)

定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

new

按照之前的原则,使用接口,并 new 一个具体实现:

1
Duck duck = new MallardDuck();

但如果出现一堆相关的具体类时,可能会写出这样的代码:

1
2
3
4
5
6
7
8
9
Duck duck;

if (picnic) { // 在野外,是绿头鸭
duck = new MallardDuck();
} else if (hunting) { // 在打猎,是诱导鸭
duck = new DecoyDuck();
} else if (inBathTub) { // 在浴缸,是橡皮鸭
duck = new RubberDuck();
}

一旦有变化或扩展,就要重新打开这段代码进行检查和修改。通常这样修改过的代码将造成部分系统更难维护和更新,而且也更容易犯错。

栗子

假设有一家比萨店,店里的披萨是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
abstract class Pizza {
protected String name; // 具体的披萨名

public void prepare() {
System.out.println("擀揉面皮,加佐料...");
}

public void bake() {
System.out.println("烘烤一段时间...");
}

public void cut() {
System.out.println("切几刀...");
}

public void box() {
System.out.println("装上盒子...");
}
}

菜单上有不同的披萨口味:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CheesePizza extends Pizza {
public CheesePizza() {
this.name = "cheese";
}
}

class GreekPizza extends Pizza {
public GreekPizza() {
this.name = "greek";
}
}

class PepperoniPizza extends Pizza {
public PepperoniPizza() {
this.name = "pepperoni";
}
}

订单系统的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;

if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

return pizza;
}
}

出现的正是上面提到的问题,既然这部分容易变化,那么可以把这段 new 的代码抽出来。

简单工厂

把不同口味的披萨放到披萨工厂里,由工厂根据客户的需求生产:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza;

if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}

return pizza;
}
}

乍一看,只是将代码转到另一个地方了,其实不然,对于披萨工厂来说,它可以面向其他的客户,比如其他小吃店,而不是限制在这间披萨店。

重写比萨店的订单代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class PizzaStore {
SimplePizzaFactory factory;

public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}

public Pizza orderPizza(String type) {
Pizza pizza = factory.createPizza(type);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

return pizza;
}
}

拓展比萨市场

现在比萨店要开分店了,比如在纽约,芝加哥等地方,而不同地方的比萨口味会有点不同,比如纽约的披萨皮薄,少芝士,而芝加哥的皮厚,多芝士。

1
2
3
4
5
6
7
8
9
10
// 继承自简单工厂的纽约披萨工厂
NYPizzaFactory nyFactory = new NYPizzaFactory();
// 纽约披萨分店
PizzaStore nyStore = new PizzaStore(nyFactory);
// 纽约风味的芝士披萨
nyStore.orderPizza("cheese");

ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore chicagoStore = new PizzaStore(chicagoFactory);
chicagoStore.orderPizza("cheese");

未完待续……

Author

Zoctan

Posted on

2018-04-01

Updated on

2023-03-14

Licensed under