再读斋

Android设计模式(五)抽象工厂模式

介绍

抽象工厂模式也是创建型设计模式之一。抽象工厂模式起源于以前对不同操作系统的图形化解决方案,如果不同操作系统中的按钮和文本控件其实现不同、展示效果也不一样,对于每一种操作系统其本身就构成一个产品类,而按钮与文本控件也构成一个产品类,两种产品类两种变化,各自有各自的特性。

定义

为创建一组相关或是相互依赖的对象提供一个接口,而不需要指定它们的具体类。

使用场景

一个对象族有相同约束时可以使用抽象工厂模式?比如Android、iOS、Window Phone下都有短信软件和拨号软件,两者都属于SoftWare软件范畴,但是它们所在的平台不一样,即便是同一家公司出品的软件,其代码实现逻辑也是不同的,这时就可以考虑使用抽象工厂方法模式来产生Android、iOS、Window Phone下的短信和拨号软件。

UML类图

UML类图如下:

主要有四类角色:

  • AbstractFactory:抽象工厂类,它声明了一组用于创建一种产品的方法,每一种方法对应一中产品,类图中分别创建产品A和产品B。

  • ConcreteFactory:具体工厂类,它实现类在抽象工厂中定义的创建产品的方法,生成一组具体产品,这些产品构成一个产品种类。如类图中的ConcreteFactory1和ConcreteFactory2.

  • AbstractProduct:抽象产品类,它为每种产品声明接口,如类图中的AbstractProductA和AbstractProductB。

  • ConcreteProduct:具体产品类,它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的方法,如类图中的ConcreteProductA1、ConcreteProductB1、ConcreteProductA2、ConcreteProductB2。

实现

在工厂模式中,以小明的车厂为例讲述了工厂方法模式,但是小明后来发现一个问题,虽然Q3、Q5都是一个车系,但是三者之间的零部件差别却是很大,使用了不同的发动机、轮胎、制动系统等零部件。Q3、Q7对应的是一系列车,而发动机、轮胎、制动系统则对应的是一系列零部件,两者是不同的产品类型,这是就可以将抽象工厂模式应用其中,首先,汽车工厂需要生产轮胎、发动机、制动系统这3种部件,然后再根据车型组装这些零部件。

  • 抽象车厂类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public abstract class CarFactory {
/*
*生产轮胎
*/
public abstract ITire createTire();
/**
* 生产发动机
* @return
*/
public abstract IEngine createEngine();
/**
* 生产制动系统
* @return
*/
public abstract IBrake createBrake();
}

下面是零部件产品类:

  • 轮胎相关类
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 interface ITire {
public void tire();
}
public class NormalTire implements ITire {
@Override
public void tire() {
// TODO Auto-generated method stub
System.out.println("普通轮胎");
}
}
public class SUVTire implements ITire{
@Override
public void tire() {
// TODO Auto-generated method stub
System.out.println("SUV 轮胎");
}
}
  • 发动机相关类
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
public interface IEngine {
public void engine();
}
public class DomesticEngine implements IEngine {
@Override
public void engine() {
// TODO Auto-generated method stub
System.out.println("国产发动机");
}
}
public class ForeignEngine implements IEngine {
@Override
public void engine() {
// TODO Auto-generated method stub
System.out.println("进口发动机");
}
}
  • 制动系统相关类
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
public interface IBrake {
public void brake();
}
public class NormalBrake implements IBrake {
@Override
public void brake() {
// TODO Auto-generated method stub
System.out.println("普通刹车");
}
}
public class SeniorBrake implements IBrake {
@Override
public void brake() {
// TODO Auto-generated method stub
System.out.println("高级刹车");
}
}

下面是具体车型的生产工厂类:

  • 生产工厂类
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
//Q3工厂类
public class Q3Factory extends CarFactory {
@Override
public ITire createTire() {
// TODO Auto-generated method stub
return new NormalTire();
}
@Override
public IEngine createEngine() {
// TODO Auto-generated method stub
return new DomesticEngine();
}
@Override
public IBrake createBrake() {
// TODO Auto-generated method stub
return new NormalBrake();
}
}
//Q7工厂类
public class Q7Factory extends CarFactory {
@Override
public ITire createTire() {
// TODO Auto-generated method stub
return new SUVTire();
}
@Override
public IEngine createEngine() {
// TODO Auto-generated method stub
return new ForeignEngine();
}
@Override
public IBrake createBrake() {
// TODO Auto-generated method stub
return new SeniorBrake();
}
}
  • 客户类
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
public class Client {
public static void main(String[] args) {
//构造Q3的工厂
CarFactory factoryQ3 = new Q3Factory();
factoryQ3.createTire().tire();;
factoryQ3.createEngine().engine();;
factoryQ3.createBrake().brake();
System.out.println("--------------------");
//构造Q7的工厂
CarFactory factoryQ7 = new Q7Factory();
factoryQ7.createTire().tire();;
factoryQ7.createEngine().engine();;
factoryQ7.createBrake().brake();;
}
}
结果打印:
普通轮胎
国产发动机
普通刹车
--------------------
SUV 轮胎
进口发动机
高级刹车

上面只是模拟了Q3和Q7的工厂,如果需要增加Q5的工厂,那么对于的轮胎、制动系统、发动机类又要增加,这里看出抽象工厂类的一个弊端就是类大量增加,如果工厂类过多,势必导致类文件非常多。因此开发中一定要权衡利弊。

总结

优点

一个显著的优点是分离接口与实现,客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已,使其从具体的产品实现中解耦,同时基于接口与实现的分离,使抽象该工厂方法模式在切换产品类时更加灵活简单。

缺点

一是类文件的爆炸性增加,二是不太容易扩展新的产品类,因为每当我们增加一个产品类就需要修改抽象工厂,那么所有的具体工厂类均会修改。

刘涤生 wechat