彻底搞懂工厂模式

定义

定义一个用于创建对象的接口,让子类决定实例化哪个类。

使用场景

在任何需要生成复杂对象的地方,都可以使用工厂方法模式。

UML类图

工厂模式UML类图

一个非常贴近生活的例子来告诉你什么是工厂模式

看到上面的定义,我相信有很多人都不明白工厂模式存在的意义到底是什么?工厂模式的存在确实是为了创建实例,但是为什么要通过工厂来创建呢,为什么不直接new一个对象呢?看起来似乎多此一举,可这真的是多此一举吗?下面我们来看两个具体的例子,通过这两个例子慢慢分析并解答我们上面所提的这些问题?

创建一个抽象动物类:Animal

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
package com.akathink.designpattern.entity;
/**
* 动物类
* @author LiuQingJie
*
*/
public abstract class Animal {
private String name;//动物的名字
public Animal() {
super();
}
public Animal(String name) {
super();
this.name = name;
}
public abstract void eat();//动物爱吃什么?
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

创建一个具体的动物类:Cat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.akathink.designpattern.entity;
/**
* 猫类
* @author LiuQingJie
*
*/
public class Cat extends Animal {
public Cat() {
super();
}
public Cat(String name){
super(name);
}
@Override
public void eat() {
System.out.println("我的名字是:" + getName() + ",喵!!"+ "I like to eat fish!");
}
}

创建一个具体的动物类:Dog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.akathink.designpattern.entity;
/**
* 狗类
* @author LiuQingJie
*
*/
public class Dog extends Animal {
public Dog() {
super();
}
public Dog(String name){
super(name);
}
@Override
public void eat() {
System.out.println("我的名字是:" + getName() + ",汪汪!!"+ "I like to eat bone!");
}
}

创建一个抽象动物工厂类:AbstractAnimalFactory

1
2
3
4
5
6
7
8
9
10
11
package com.akathink.designpattern;
import com.akathink.designpattern.entity.Animal;
/**
* 抽象动物工厂:用来生产小动物
* @author LiuQingJie
*
*/
public abstract class AbstractAnimalFactory {
public abstract <T extends Animal> T createAnimal(Class<T> c);
}

创建一个具体动物工厂类:AnimalFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.akathink.designpattern;
import com.akathink.designpattern.entity.Animal;
/**
/**
* 动物工厂:用来生产小动物
* @author LiuQingJie
*
*/
public class AnimalFactory extends AbstractAnimalFactory {
@SuppressWarnings("unchecked")
@Override
public <T extends Animal> T createAnimal(Class<T> c) {
Animal animal = null;
try {
animal = (T)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) animal;
}
}

创建一个工厂模式场景类:FactoryPattern

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
package com.akathink.designpattern;
import com.akathink.designpattern.entity.Animal;
import com.akathink.designpattern.entity.Cat;
import com.akathink.designpattern.entity.Dog;
/**
* 工厂模式场景类
* @author LiuQingJie
*
*/
public class FactoryPattern {
public static void main(String[] args) {
//创建一个动物工厂
AbstractAnimalFactory animalFactory = new AnimalFactory();
//通过动物工厂创建一个Tom猫
Animal cat = animalFactory.createAnimal(Cat.class);
cat.setName("Tom");
cat.eat();
//通过动物工厂创建一个哮天犬
Animal dog = animalFactory.createAnimal(Dog.class);
dog.setName("哮天犬");
dog.eat();
}
}

运行结果:

1
2
我的名字是:Tom,喵!!I like to eat fish!
我的名字是:哮天犬,汪汪!!I like to eat bone!

上面这个例子确实是采用工厂模式来实现的,但是我们依然看不出来,为什么要采用工厂模式来实现,这样实现的好处有哪些?网上各大博客关于工厂模式的例子很多都和此类似,并没能具体说明,更甚者没能说明白工厂模式到底使用在什么地方。其实工厂模式在我们现实生活中非常常见,下面举的这个例子将会告诉大家工厂模式到底用在哪里?

相信大家都去过麦当劳吧,我们去点餐的时候,可以点一份香草奶昔、一份麦辣鸡翅,再加一杯咖啡,也可以点一个吉士汉堡包和一杯可口可乐。具体要点哪些,我们可以随意挑、随意点,挑完之后直接告诉点餐员,然后付款就OK了。这时候我们可能发现这不是传说中的建造者模式吗?与工厂模式有什么关系呢?建造者模式与工厂模式确实没啥太大关系,但是通过这个例子却同时可以引出我们的工厂模式。好了,工厂模式隆重现身。

有的时候我们上班都很累,上完一天的班了就是一阵猛饿,到了麦当劳之后,发现自己并不知到吃啥(不会饿昏了头吧),怎么办呢,没办法,就叫一份今日推荐的麦辣鸡腿汉堡超值套餐吧。这个时候我们并不需要把套餐里面的每类食物都再说一遍,只需要点这一份套餐就可以了。

假设我们现在只提供三种类型点食物:汉堡、饮料、小吃

汉堡(巨无霸、吉士汉堡、双层吉士汉堡)

1
2
3
4
5
6
7
package com.akathink.designpattern.entity;
/**
* 汉堡
*/
public interface IBurgers {
String makeBurger();
}

1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class BigMac implements IBurgers {
@Override
public String makeBurger() {
return "巨无霸";
}
}
1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class CheeseBurger implements IBurgers {
@Override
public String makeBurger() {
return "吉士汉堡包";
}
}
1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class DoubleCheeseBurger implements IBurgers {
@Override
public String makeBurger() {
return "双层吉士汉堡";
}
}

饮料(可乐、牛奶、橙汁)

1
2
3
4
5
6
7
package com.akathink.designpattern.entity;
/**
* 饮料
*/
public interface IBeverages {
String makeDrinking();
}

1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class Coke implements IBeverages {
@Override
public String makeDrinking() {
return "可乐";
}
}
1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class Milk implements IBeverages {
@Override
public String makeDrinking() {
return "牛奶";
}
}
1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class OrangeJuice implements IBeverages {
@Override
public String makeDrinking() {
return "橙汁";
}
}

小吃(奶昔、巧克力奶昔、苹果派)

1
2
3
4
5
6
7
8
package com.akathink.designpattern.entity;
/**
* 小吃
*/
public interface ISnacks {
String makeSnack();
}

1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class MilkSnack implements ISnacks {
@Override
public String makeSnack() {
return "奶昔";
}
}
1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class ChocolateSnack implements ISnacks {
@Override
public String makeSnack() {
return "巧克力奶昔" ;
}
}
1
2
3
4
5
6
7
8
9
10
package com.akathink.designpattern.entity;
public class ApplePie implements ISnacks {
@Override
public String makeSnack() {
return "苹果派";
}
}

食物准备好了,还需要一个订单类,因为这些食物都是客户自选组合点,所以我们点订单类可以使用建造者模式,更多关于建造者模式,请关注下一篇设计模式文章。

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
package com.akathink.designpattern;
import com.akathink.designpattern.entity.IBeverages;
import com.akathink.designpattern.entity.IBurgers;
import com.akathink.designpattern.entity.ISnacks;
public class Order {
private IBurgers mBurger;
private IBeverages mBeverages;
private ISnacks mSnack;
private Order(OrderBuilder builder) {
mBurger = builder.mBurger;
mBeverages = builder.mBeverages;
mSnack = builder.mSnack;
}
public String makeOrder() {
StringBuilder sb = new StringBuilder();
if (mBurger != null) {
sb.append(mBurger.makeBurger()).append(" ");
}
if (mBeverages != null) {
sb.append(mBeverages.makeDrinking()).append(" ");
}
if (mSnack != null) {
sb.append(mSnack.makeSnack());
}
return sb.toString();
}
public static class OrderBuilder {
private IBurgers mBurger;
private IBeverages mBeverages;
private ISnacks mSnack;
public OrderBuilder() {
}
public OrderBuilder addBurger(IBurgers burgers) {
this.mBurger = burgers;
return this;
}
public OrderBuilder addBeverage(IBeverages beverages) {
this.mBeverages = beverages;
return this;
}
public OrderBuilder addSnack(ISnacks snacks) {
this.mSnack = snacks;
return this;
}
public Order build() {
return new Order(this);
}
}
}

订单工厂

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
package com.akathink.designpattern;
import com.akathink.designpattern.entity.ApplePie;
import com.akathink.designpattern.entity.BigMac;
import com.akathink.designpattern.entity.CheeseBurger;
import com.akathink.designpattern.entity.ChocolateSnack;
import com.akathink.designpattern.entity.Coke;
import com.akathink.designpattern.entity.DoubleCheeseBurger;
import com.akathink.designpattern.entity.Milk;
import com.akathink.designpattern.entity.MilkSnack;
import com.akathink.designpattern.entity.OrangeJuice;
public class OrderFactory {
// 创建一份巨无霸套餐(巨无霸+可乐+苹果派)
public static Order createBigMacCombo() {
return new Order.OrderBuilder().addBurger(new BigMac()).addBeverage(new Coke()).addSnack(new ApplePie())
.build();
}
// 创建一份吉士汉堡套餐(吉士汉堡+牛奶+奶昔)
public static Order createCheeseBurgerCombo() {
return new Order.OrderBuilder().addBurger(new CheeseBurger()).addBeverage(new Milk()).addSnack(new MilkSnack())
.build();
}
// 创建一份双层吉士汉堡套餐(双层吉士汉堡+橙汁+巧克力奶昔)
public static Order createDoubleBurgerCombo() {
return new Order.OrderBuilder().addBurger(new DoubleCheeseBurger()).addBeverage(new OrangeJuice())
.addSnack(new ChocolateSnack()).build();
}
}

场景类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.akathink.designpattern;
public class FactoryClient {
public static void main(String[] args) {
// 通过订单工厂创建一份巨无霸套餐
Order bigMacComboOrder = OrderFactory.createBigMacCombo();
System.out.println(bigMacComboOrder.makeOrder());
// 通过订单工厂创建一份巨无霸套餐
Order cheeseBurgerComboOrder = OrderFactory.createCheeseBurgerCombo();
System.out.println(cheeseBurgerComboOrder.makeOrder());
// 通过订单工厂创建一份双层吉士汉堡套餐
Order DoubleBurgerComboOrder = OrderFactory.createDoubleBurgerCombo();
System.out.println(DoubleBurgerComboOrder.makeOrder());
}
}

运行结果:

1
2
3
巨无霸 可乐 苹果派
吉士汉堡包 牛奶 奶昔
双层吉士汉堡 橙汁 巧克力奶昔

至此,我们可以发现,如果我们点一份套餐,可以很容易创建一个实例对象,而不用去关心创建这个对象时需要配置哪些东西,或者内部是如何创建的。这样就把一个复杂的对象交给工厂类来负责了,同时通过工厂类,我们也能快速了解我们的工厂能够创建哪些对象,换句话也就是能够提供哪些服务。假如以后我们工厂里面的某个服务更改了,只需要更改工厂模式一处位置就可以了,也就实现了可维护性。

所以,回归到工厂模式的应用场景:在任何需要生成复杂对象的地方,都可以使用工厂方法模式。

工厂模式的优点

优点

  • 良好的封装性,代码结构清晰。创建一个对象是有条件约束的,假如我们需要创建一个具体的对象,只要知道这个产品的类名就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
  • 扩展性强。假如我们想要增加一个产品类,只要适当的修改具体的工厂类,或者扩展一个工厂类,就可以完成“拥抱变化”。
  • 屏蔽产品类。产品类的实现如何变化,调用者不需要关心,只需要关心产品的接口就行了。
  • 工厂方法模式是典型的解耦框架。高层模块需要知道产品的抽象类,其他的实现类都不需要关心,符合迪米特原则;也符合依赖倒置原则,只依赖产品的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类是没有任何问题的。

参考资料

《设计模式之禅》
《大话设计模式》
《Android源码设计模式》
http://blog.csdn.net/renhui999/article/details/8482977
http://blog.csdn.net/nugongahou110/article/details/50425823
http://blog.csdn.net/lovelion/article/details/9300731