工厂模式

在之前的章节中,我们提到了要针对接口编程,所以在DuckDemo中,我们利用了接口的多态的特性,可以动态的改变对象。但是这样也是存在一定的问题的,下面我们就来分析这个问题。

一个pizza引发的血案

假设我们开了一个pizza店,我们提供了一个程序能够让顾客自动下单订购pizza,那么我们的代码可以如下实现:
首先我们定义一个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
package com.changyuan.pizzaDemo;

import java.util.ArrayList;

public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();

void prepare() {
System.out.println("Prepare " + name + ".");
}

void bake() {
System.out.println("Bake pizza for 25 minutes.");
}

void cut() {
System.out.println("Cut pizza to 8 slices.");
}

void box() {
System.out.println("Boxing pizza");
}

public String getName() {
return name;
}
}

然后定义一个pizzaStore的类:

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
package com.changyuan.pizzaDemo;

public class PizzaStore {
public Pizza createPizza(String style, String type) {
Pizza pizza = null;
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new NYStyleClamPizza();
}
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
}
} else {
System.out.println("Error: invalid pizza type");
return pizza;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

我们看这个类里面的createPizza的方法,我们根据不同的参数new不同的类的实例,NYStyleCheesePizza等这六个类都是继承pizza类的具体的子类。这里我们利用接口的多态思想,可以动态的调用具体的方法。

但是当我们需要添加新的类型的pizza时怎么办呢?比如我们需要添加Miami的cheese口味的pizza,那么就必须要修改createPizza的方法,在里面继续添加if。这就违背了我们的一个重要的设计原则

对修改关闭,对扩展开放

因此我们需要引入新的设计模式来解决这个问题,这就是工厂模式。

上面的代码归档位置

https://github.com/changyuanchn/DesignPattern/tree/main/src/com/changyuan/pizzaDemo

我们看上面的代码存在什么问题?

就一个问题,我们在针对具体的实现再编程,也就是我们在具体的new每一个类的实例。我们接触到的第一个原则是:

找出变化的部分

而这里面变化的部分就是createPizza方法。这里我们引入工厂(工厂处理创建对象的细节),来统一处理这个变化的部分。

我们定义一个工厂类来处理对象的创建

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
public class SimplePizzaFactory {
public Pizza createPizza(String style, String type) {
Pizza pizza = null;
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new NYStyleClamPizza();
}
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
}
} else {
System.out.println("Error: invalid pizza type");
}
return pizza;
}
}

然后就可以改造PizzaStore了。

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

public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String style, String type) {
Pizza pizza;
pizza = factory.createPizza(style, type);

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

这样我们就把需要修改的部分单独提出来了,放到了工厂类中了。

那么这样有什么好处呢?好像没什么啊,就是单独的将变化的部分放到了一个类中。实际上这只是一点小改变,当我们需要添加新的加盟店铺时,就只需要使用SimplePizzaFactory创建就好了,尤其是当需要修改种类时,只需要修改SimplePizzaFactory就好了,不用修改两个类。

类图如下所示

simplePizzaFactory

代码归档如下

https://github.com/changyuanchn/DesignPattern/tree/main/src/com/changyuan/pizzaSimpleFactoryDemo

当我们真的有了两个加盟店后,我们可以直接调用SimplePizzaFactory去创建对象,但是有个问题哎,就是每个加盟店修改自己的内容,比如说pizza的种类等等,它只能修改SimplePizzaFactory类,这必然会影响到别的加盟店,那么我们就需要方式去解决这个问题。

在上面的代码中,我们每次创建一个实例时,都是用的同一个pizzaStore

1
2
3
4
5
SimplePizzaFactory factory = new SimplePizzaFactory();
PizzaStore nyStore = new PizzaStore(factory);
nyStore.orderPizza("NY", "cheese");
PizzaStore chicagoStore = new PizzaStore(factory);
chicagoStore.orderPizza("Chicago", "veggie");

我们这里要做的就是对于每个加盟店,创建其自己的store类(继承自pizzaStore),然后在子类中自己创建createPizza方法,让子类自己决定其创建的对象。

pizzaStore类:

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String type);
}

每个店铺有自己的createPizza方法:

1
2
3
4
5
6
7
8
9
10
11
12
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (type.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (type.equals("clam")) {
return new NYStyleClamPizza();
}
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class ChicagoPizzaStore extends PizzaStore {
Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) {
return new ChicagoStyleVeggiePizza();
} else if (type.equals("clam")) {
return new ChicagoStyleClamPizza();
}
return null;
}
}

代码归档如下

https://github.com/changyuanchn/DesignPattern/tree/main/src/com/changyuan/pizzaFactory

工厂模式

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

工厂模式一般有两个类,一个是创建者类,一个是产品类。

我们回到我们最初的代码,假设我们要在其中加入一种California风味的pizza,那么就需要修改PizzaStore类。实际上这个版本的pizzaStore依赖于所有的pizza对象,因为在这个函数里,直接创建了这些pizza对象。而且每新增一个pizza种类,就会让pizzaStore多一个依赖。

pizzaStoreOrigin

而我们通过工厂模式,我们的pizzaStore依赖于抽象的pizza类,而具体的pizza类也依赖于抽象的pizza类(因为它们实现了pizza的接口)。高层组件和底层组件都依赖于抽象的pizza类,这就是依赖倒置原则。

pizzaStoreFactory

第五个设计原则:

依赖倒置原则:要依赖抽象,不要依赖具体的类

看看我们当前的实现,对于不同的pizza类型,它们的原料都是在类中写死的。换句话说,它们都是依赖于各自的pizza类的。那么如果我们要将其统一管理起来要怎样处理呢?

假设我们有两个pizzaStore的店铺,它们分别使用相同的原料家族,但是具体的每个原料是不同的,那么我们首先需要创建基础的原料接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Dough {
public String toString();
}

public interface Clam {
public String toString();
}
public interface Cheese {
public String toString();
}
public interface Pepperoni {
public String toString();
}
public interface Veggies {
public String toString();
}

然后我们可以定义具体的原料类

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 ThinCrustDough implements Dough {
public String toString() {
return "Thin Crust Dough";
}
}
public class ThickCrustDough implements Dough {
public String toString() {
return "Thick Crust Dough";
}
}
public class Spinach implements Veggies {
@Override
public String toString() {
return "Spinach";
}
}
public class SlicedPepperoni implements Pepperoni {
public String toString() {
return "Sliced Pepperoni";
}
}
public class ReggianoCheese implements Cheese {
public String toString() {
return "Reggiano Cheese";
}
}
public class RedPepper implements Veggies {
public String toString() {
return "RedPepper";
}
}

等等等等的具体的原料类型。

然后我们可以创建我们的pizzaStore

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 class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clam createClam() {
return new FreshClams();
}
}

public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory
{
public Dough createDough() {
return new ThickCrustDough();
}
public Sauce createSauce() {
return new PlumTomatoSauce();
}
public Cheese createCheese() {
return new MozzarellaCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] = { new BlackOlives(), new Spinach(), new Eggplant() };
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clam createClam() {
return new FrozenClams();
}
}

我们可以看到不同的原料工厂会使用不同的原料类型。

然后我们来看我们的pizzaStore,每个pizzaStore都会调用自己的PizzaIngredientFactory来生产所需要的原料,然后将原料作为输入送到各中pizza制作的函数中,这样就形成了口味不同的pizza。虽然调用的是同一个CheesePizza接口,但是由于原料的不同,进儿产生了不同的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
public class ChicagoPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();

if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("Chicago Style Cheese Pizza");
} else if (item.equals("veggies")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("Chicago Style veggies Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("Chicago Style clam Pizza");
} else if (item.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("Chicago Style pepperoni Pizza");
}
return pizza;
}
}
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();

if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("NY Style Cheese Pizza");
} else if (item.equals("veggies")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("NY Style veggies Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("NY Style clam Pizza");
} else if (item.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("NY Style pepperoni Pizza");
}
return pizza;
}
}

而我们的各种pizza子类,也继承自pizza类,在子类中实现各自的prepare方法,这样我们在测试用例里面就可以直接调用orderPizza方法来实现不同的pizza类型的prepare函数调用了。

代码归档路径

https://github.com/changyuanchn/DesignPattern/tree/main/src/com/changyuan/pizzaAbstractFactory

抽象工厂模式

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

上面的原料模型代码就是抽象工厂的应用。

比较一下工厂方法和抽象工厂方法

在工厂方法中,我们创建了一个工厂类,来创建所需要的pizza类型

1
2
3
4
5
6
7
8
9
10
11
12
public class ChicagoPizzaStore extends PizzaStore {
Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) {
return new ChicagoStyleVeggiePizza();
} else if (type.equals("clam")) {
return new ChicagoStyleClamPizza();
}
return null;
}
}

而PizzaStore中,也是实际上调用的工厂类的createPizza方法

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
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
22
23
24
25
26
27
28
29
30
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clam createClam();
}

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clam createClam() {
return new FreshClams();
}
}

而当我们具体创建各个原料类的时候,实际上new的是各个子类

1
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();

而在使用时则直接调用各个子类中的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CheesePizza extends Pizza {
PizzaIngredientFactory pizzaIngredientFactory;

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

void prepare() {
System.out.println("Prepare " + name);
dough = pizzaIngredientFactory.createDough();
sauce = pizzaIngredientFactory.createSauce();
cheese = pizzaIngredientFactory.createCheese();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ChicagoPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();

if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("Chicago Style Cheese Pizza");
} else if (item.equals("veggies")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("Chicago Style veggies Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("Chicago Style clam Pizza");
} else if (item.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("Chicago Style pepperoni Pizza");
}
return pizza;
}
}

一句话就是一个是类,一个是接口(定义了一族类),工厂方法用的是继承,而抽象工厂方法用的是组合。

两种模式的类图如下所示:

Factory

abstractFactory

显示 Gitment 评论