工厂模式

简介

工厂模式,顾名思义,就是制造对象的工厂。在学习之前,是存在很大的误解的,之前认识的工厂是简单工厂,它并不属于工厂模式的范畴,真正的工厂模式有两种,分别是工厂方法模式抽象工厂模式

工厂方法模式是使用抽象方法,使用子类继承的方式去实现。抽象工厂模式是抽象出一个工厂的接口,用具体的工厂去实现接口,然后采用组合的方式,去解藕。总而言之,两者使用的场景不一样,感觉差别挺大。

静态工厂

本章案例是制造Pizza,讲解的思路是:一个店面给顾客制造不同口味的披萨,发现效益不错,别的地方的商店想加盟,希望也能套用你的模板,再到不同地方的商店希望在本地有对应原料加工工厂。可见,随着业务的扩张,带来的变化变化也越来越多,这个时候各个模式就在应对这种变化中产生了静态工厂工厂方法抽象工厂

首先是静态工厂,在使用静态工厂前,顾客下订单的实现应该是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PizzaStore {

Pizza orderPizza(String type) {

Pizza pizza = null;
// 变化的部分
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("peperoni".equals(type)) {
pizza = new PepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ClamPizza();
}

// 不变的部分
pizza.preprae();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

在上面代码中,我们发现ifelse代码是经常发生改变的,每增删一种口味的Pizza就要修改这段代码,这必将造成系统难以维护。在策略模式中学到了应该将变化的部分和不变的部分分离出来,那么我们对这部分代码做一个封装,这就是简单工厂了,单独负责Pizza的创建。

实现的代码如下:

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

public Pizza create(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("peperoni".equals(type)) {
pizza = new PepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ClamPizza();
} else if ("veggie".equals(type)) {
pizza = new VeggiePizza();
} else {
System.out.println("本店没有该口味披萨,请换购其他类型");
}
return pizza;
}
}

当新增新口味的Pizza时,我们只需修改这里的代码,create方法可以声明为静态的,当调用的时候可以不需要创建对象,直接调用。但这种设计不容易扩展,运行的时候不能修改create的行为。

那么,当有不同的加盟商到来时,这个时候create方法不得不变,要怎么办呢?这个时候我们可以这样做:

但是,更大的变化再等着我们,有些加盟商说,想要自己的一套bake、cut、box的方式。

工厂方法

工厂方法要求不同的厂商提供自己的create方法,同时可以提供自己一套烘烤、切片、包装的方式。这个时候,工厂方法来解救我们。

简单工厂将create方法进行了抽象,交给子类去实现。当定义的Pizza有自己的烘烤、切片、包装方式时,在子类定义的时候需要进行覆盖。

实现为:

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

Pizza orderPizza(String type) {

// 变化的一部分
Pizza pizza = creatPizza(type);
// 不变的一部分
pizza.preprae();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

abstract Pizza creatPizza(String type);
}

加盟商只需实现该方法,当该加盟商新增一种口味时,只需在以下实现中做一下修改,相比于简单工厂的方式,更加易于维护,而且对系统的影响度更低。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class NYStylePizzaStore extends PizzaStore {

@Override
Pizza creatPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new NYStyleCheesePizza();
} else if ("peperoni".equals(type)) {
pizza = new NYStylePepperoniPizza();
}
return pizza;
}
}

讲到这里,是不是对工厂方法有了大概的了解,我们来看看到底什么是工厂方法吧!

工厂方法模式 书上定义为:

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

到这里,可以回顾一下,如果使用简单工厂去实现,会是什么样的?会出现一个高度依赖的工厂类,当新增或删减时,对系统的影响是很大的。这样使得PizzaStore类高度依赖所有的Pizza对象,这里违背了我们新学到的一个设计原则依赖倒置原则

要依赖抽象,不要依赖具体类。

这个原则说明了:不能让高层组建依赖低层组件,而且,不管高层或底层组件,两者都应该依赖于抽象

所谓高层组件,是由其底层组件定义其行为的类。在这里,PizzaStore是高层组件,因为它的行为是由Pizza定义的,PizzaStore创建所有不同的Pizza对象,准备、烘烤、切片、装盒;而Pizza本身属于底层组件。

抽象工厂

到这里,系统已经有了良好的弹性框架,而且遵循设计原则。但是,我们的设计远远没有结束,比萨店成功的关键在于新鲜、高质量的原料,要是在当地建立一个原料的工厂,还能实现更大的利润。

我们首先得定义原料工厂:

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

Dough createDough();

Sauce createSauce();

Cheese createCheese();

VeggiePizza createVeggiePizza();

Pepperoni createPepperoni();

Clams createClams();
}

将Pizza的parepar进行抽象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public abstract class Pizza {

String name;

// 面团
Dough dough;

// 酱料
Sauce sauce;

// 一套佐料
Cheese cheese;

Pepperoni pepperoni;

Clams clams;

abstract void preprae();
}

其实这里就有点类似于工厂方法了,交给子类去实现

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

PizzaIngredientFactory pizzaIngredientFactory;

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

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

最后定义具体的PizzaStore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class NYPizzaStore extends PizzaStore {

PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();

@Override
Pizza creatPizza(String type) {
Pizza pizza = null;

if ("cheese".equals(type)) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("NY Style Cheese Pizza");
} else if ("clam".equals(type)) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("NY Style Clam Pizza");
}
return pizza;
}
}

最后测试一下:

1
2
PizzaStore pizzaStore = new NYPizzaStore();
Pizza pizza = pizzaStore.orderPizza("cheese");

可以跟踪一下代码执行

  • 先是创建一个NYPizzaStore实例:PizzaStore pizzaStore = new NYPizzaStore();

  • 有了Pizza店了,接受订单:pizzaStore.orderPizza("cheese");

  • orderPizza会先调用creatPizza方法:Pizza pizza = creatPizza(type);

  • creatPizza方法调用时,涉及到原料工厂了:pizza = new CheesePizza(ingredientFactory);

  • 拿到Pizza后,会调用prepare方法:

    1
    2
    3
    4
    5
    6
    void preprae() {
    System.out.println("Preparing " + name);
    dough = pizzaIngredientFactory.createDough();
    sauce = pizzaIngredientFactory.createSauce();
    cheese = pizzaIngredientFactory.createCheese();
    }
  • 最后,得到准备好的Pizza,orderPizza就会接着烘烤、切片、装盒。

抽象工厂模式在书上定义为:

提供一个接口,用于创建相关或依赖对象的家族,而不需要指定具体类。

代码链接

模型如图所示:

Related Issues not found

Please contact @alongsocjr to initialize the comment