工厂模式

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

new对象的问题

当使用new 关键之创建一个对象的时候,你是在实例化一个具体的类,你使用的是实现,而不是接口。代码绑定着具体的类会导致代码更脆弱,更缺乏弹性。

例如如下代码,有一大堆不同的鸭子实现类时,但是必须等到运行时,才知道该实例化哪一个

1
2
3
4
5
6
7
8
Duck duck;
if (picnic) {
duck = new MallardDuck();
} else if (hunting) {
duck = new DecoyDuck();
} else if (inBathTub) {
duck = new RubberDuck();
}

当看到这样的代码,一旦有变化或者扩展,就必须冲洗打卡这段代码进行检查和修改,通常修改这样的代码将造成部分系统更难维护和更新,而且也更容易犯错。
在技术上new 对象没有错,毕竟这是Java的基础部分,真正的的问题是我们的程序需要改变。针对接口编程,可以隔离掉以后系统可能发生的一大堆改变。如果代码是针对接口而写,那么通过多态,它可以与任何新类实现该接口,但是当代码使用大量的具体类时,就是自找麻烦,因为一旦加入具体的类,就必须改变代码,也就是说你的代码并非”对修改关闭”

简单工厂

简单工厂只负责创建对象,这个工厂可能不止服务于一个地方,以后对象类型如果有变动,只需修改简单工厂的逻辑就可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SimplePizzaFactory {
public Pizza createPizza(String type){
Pizza pizza=null;
if("".equals(type)){
pizza= new CheesePizza();
}else if ("Clam".equals(type)){
pizza=new ClamPizza();
}else if ("Greek".equals(type)){
pizza=new GreekPizza();
}
return pizza;
}
}

使用简单工厂,将new对象的操作移动到了简单工厂中,这里只传入订单类型来使用工厂创建对象

1
2
3
4
5
6
7
8
9
10
11
12
public class SimplePizzaStore {
SimplePizzaFactory simplePizzaFactory;
public SimplePizzaStore(SimplePizzaFactory factory){
this.simplePizzaFactory=factory;
}
public Pizza orderPizza(String type){
Pizza pizza=this.simplePizzaFactory.createPizza(type);
return pizza;
}
}

简单工厂其实是一种常用的编程习惯,而不是一种设计模式。

工厂方法

使用简单工厂只能穿件简单工厂中一种模式的对象,现在我们想使用不同的工厂创建不同的对象

1
2
3
4
5
6
7
8
9
public abstract class AbstractPizzaStore {
public final Pizza orderPizza(String type){
Pizza pizza=createPizza(type);
return pizza;
}
protected abstract Pizza createPizza(String type);
}

将创建pizza对象的方法抽象到PizzaStore中,然后让具体的子类去实现不同的创建方式,同时,将orderPizza方法在本类中设置为final,这个方法因为比较固定, 防止子类覆盖掉该方法

具体的pizza 店根据自己的需求创建不同风味的pizza

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ChinesePizzaStore extends AbstractPizzaStore {
@Override
protected Pizza createPizza(String type) {
if ("chesses".equals(type)) {
return new ChineseStyleCheesePizza();
} else if ("clam".equals(type)) {
return new ChineseStyleClamPizza();
} else {
return new ChineseStyleCheesePizza();
}
}
}

测试入口

1
2
3
4
5
6
public class Main{
public static void main(String[] args){
PizzStore pizzStore=new ChinesePizzStore();
pizzStore.orderPizza("cheese");
}
}

工厂方法用来处理对象的创建,并将这样的行为封装在子类中,这样,客户程序中关于超类的代码就和子类对象创建代码解耦了

工厂方法中抽象方法原型
工厂方法中抽象方法原型

所有工厂模式都用来封装对象的创建,工厂方法模式,通过让子类决定该创建什么样的对象,来达到将对象创建过程封装的目的,让我们来看看这些类图。



工厂方法模式能够封装具体类型的实例化,看看下面的类图,抽象的Creator提供了一个创建对象的方法接口,也称为”工厂方法”,在抽象的Creator中,任何其他实现的方法,都可以使用到这个工厂方法所制造出来的产品,但是只有子类真正实现这个工厂方法并穿件产品。

抽象工厂

从上面的代码中可以看到,PizzaStore中依赖了具体的Pizza类。而设计原则中有一条要依赖抽象类,不要依赖具体的类,这也是依赖倒置原则(Dependency Inversion Principle),这个原则应该告诉我们重写代码以便于我们依赖抽象类,而不是具体的类。对于高层次及低层次模块都应该如此。

原则的应用

非常依赖PizzStore的主要问题在于,它依赖每个披萨类型,因为它是在自己的orderPizza方法中。实例化这些具体类型。

在为各种不同的PizzStore提供不同的原料时,代码如下

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
62
63
64
65
66
67
68
69
70
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Clam createClam();
}
public class NYPizzaIngredientFactory 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 Clam createClam() {
return new FreshClams();
}
}
public class CheesePizza extends AbstractPizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory factory) {
this.ingredientFactory = factory;
}
@Override
void prepare() {
log.info("Preparing ....");
super.dough = this.ingredientFactory.createDough();
super.cheese = this.ingredientFactory.createCheese();
super.clam = this.ingredientFactory.createClam();
super.sauce = this.ingredientFactory.createSauce();
}
}
}
public class NYPizzaStore extends AbstractPizzaStore {
public class NYPizzaStore extends AbstractPizzaStore {
@Override
protected Pizza createPizza(String item) {
Pizza pizza=null;
PizzaIngredientFactory ingredientFactory=new NYPizzaIngredientFactory();
if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
}
return pizza;
}
}

我们引入了新类型的工厂,也就是所谓的抽象工厂,来创建原料家族。通过抽象工厂所提供的接口,可以创建产品的家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。因为代码从实际的产品中解耦了,所以我们可以替换不同的工厂来取得不同的行为。
AbstractPizza 的代码利用相关的工厂生产原料。所生产的原料依赖所使用的工厂,Pizza类根本不关心这些原料。它只知道如何制作披萨。现在Pizza和区域原料之间被解耦。
下面是抽象工厂的类图关系

抽象工厂
抽象工厂

抽象工厂定义

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