0%

设计模式笔记(2)——工厂模式

一. 工厂模式概念

工厂模式是一种创建型模式,他的意义是专门创建一个类,我们在这个类中去完成我们需要创建的对象,这样既可以方便管理,又可以提高代码的可扩展性和灵活性。工厂模式包括简单工厂模式、工厂方法模式、抽象工厂模式,其中工厂方法模式就是我们常说的工厂模式。

我们在开发初期通常是直接通过new来创建一个对象的,这样的方式无疑是最简单的,但是这样创建对象有很多不足的地方,我们可以通过一个例子来看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

class LoginAction {
private UserDAO udao;

public LoginAction() {
udao = new JDBCUserDAO(); //创建对象
}

public void execute() {
//其他代码
udao.findUserById(); //使用对象
//其他代码
}

我们在LoginAction类中创建了一哦个UserDao对象udao,并在execute()方法中调用了udao对象的findUserById()方法。这样LoginAction对udao既有创建职责又有使用职责,在同一个类中将两种职责耦合在了一起,当我们有修改需求的时候,比如我们要创建的是UserDAO的子类的时候,我们就需要修改LoginAction()中的代码,违背了开闭原则。

而工厂模式是通常解决这个问题好方法,我们可以创建一个UserDaoFactory由它来专门负责UserDao类及其子类对象的创建。当我们需要增加UserDao子类或者修改构造函数的时候,我们只需要维护UserDaoFactory而不影响其他相关类的使用。工厂模式一只强调的是当两个类A和B发生关联的时候,两者只会发生A使用B或者A创建B这两种关系,而不是同时发生。 并且工厂类帮我们封装了创建对象的细节,我们只要传递相应的参数,就可以帮助我们创建具体的对象。下面我们来介绍依次介绍3个工厂工行模式。

二. 简单工厂模式

简单工厂模式又叫做静态工厂模式,他并不属于23种经典模式之中,我们从一个例子中先体验简单工厂模式。

比如说我们需要生产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
31
32
33

public abstract class Pizza {


private String name;
abstract public void prepare();
public void eat(){
System.out.println("开动啦!吃:" + name);
}

public String getName() {
return name;
}

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

public class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备一个奶酪披萨");
}
}

public class GreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备一个希腊披萨");
}
}

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

public class SimpleFactory {

static public Pizza createPizza(String name){
Pizza pizza =null;

if(name.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
pizza.prepare();
}else if (name.equals("greek")){
pizza = new GreekPizza();
pizza.setName("希腊披萨");
pizza.prepare();
}
return pizza;
}

}


1
2
3
4
5
6
7
8
9
10
11
public class OrderPizza {
public static void main(String[] args) {
Pizza pizza = SimpleFactory.createPizza("greek");
if(pizza!=null){
pizza.eat();
}else {
System.out.println("没有pizza");
}
}
}

当我们在订单中需要创建一个pizza的时候,我们会调用SimpleFactory中的方法createPizza来创建一个对象,SimpleFactory会根据参数来自主判断创建对象的类型。

这种方法使用起来比较简单,表面上解决了OrderPizza中耦合的问题,但是他只是将逻辑判断中的代码转移到了工厂中,这会使得工厂类不满足ocp原则,在需要修改代码的时候我们需要维护工厂类的逻辑判断。

二. 工厂方法模式

为了解决简单工厂模式的缺点,使得工厂类更加符合开闭原则。我们先使用一个抽象类去定义一个抽象的创建对象的方法,再由子类去继承这个抽象类,去专门创建具体的对象。

还是上一个例子,但是我们分别创建了奶酪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
public abstract class Factory {

public abstract Pizza createPizza();

}

public class GreekPizzaFactory extends Factory{

@Override
public Pizza createPizza() {
GreekPizza greekPizza = new GreekPizza();
greekPizza.setName("希腊披萨");
return greekPizza;
}
}

public class CheesePizzaFactory extends Factory{

@Override
public Pizza createPizza() {
return new CheesePizza();
}
}

这样写的好处就是,我们在增加或者删除不同口味pizza的时候,我们只需要创建不同的工厂去生产不同的pizza就可以了,而不用去修改factory中的代码。但是工厂方法模式也有一定的缺陷,就是当我们的产品增多时我们需要创建大量的具体工厂去生产不同的对象,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。

二. 抽象工厂模式

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。

为了更清晰地理解工厂方法模式,需要先引入两个概念:

  • 产品等级结构 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
  • 产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。

抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。

抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
这里我们看一个uml图理解一下

upload successful

当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。

增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

参考文献

视频:
尚硅谷Java设计模式,韩顺平图解java设计模式

文献:
3. 抽象工厂模式(Abstract Factory)

深入理解工厂模式

创建对象与使用对象——谈谈工厂的作用