设计模式--工厂方法模式

说到工厂方法模式不得不围绕车去说明了,因为它太通俗了,不需要过多去解释,满大街都是的;就拿宝马来讲,这是德国的一个品牌,虽然不算太好,但也还可以,记得当初宝马在德国慕尼黑新建第一家工厂的时候,组织架构还是非常简单的,分为设计部、生产部、装配部、测试部、订单部这几个部门,所以通过这几个部门也大致可以了解到一辆BMW的生产流程时设计-生产-装配-测试;然后有人下订单后就把车送过去了。

首先建造一个汽车工厂首先得知道你生产什么东西吧,也就是说你得提前设计你的汽车吧,所以我们需要定义一个BMW接口,用来统一定义BMW汽车共性的东西(打开车门、启动、开走),这三部曲是必须要做的。

1、新建一个BMW接口

通过一个接口及行为可以大致描述你的车时什么样子的,打开车门表示车肯定要有门,启动则表示需要有一个发动机,开走则表示车需要有轮子;暂且这么想吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 通用的BMW汽车接口
* @author mapingsheng
*/
public interface BMW {
/**
* 打开车门
*/
public void open();
/**
* 启动
*/
public void start();
/**
* 开走
*/
public void go();
}

2、新建一个具体的BMW车-BMWx6

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
/**
* BMWx6系列汽车
* @author mapingsheng
*/
public class BMWx6 implements BMW{
BMWx6(){
System.out.println("--------BMWx6---------");
open();
start();
go();
System.out.println("--------BMWx6---------");
}
/**
* 重写具体的打开方式
*/
public void open(){
System.out.println("手动打开车门");
}
/**
* 重写具体的启动方式
*/
public void start(){
System.out.println("拧钥匙启动");
}
/**
* 重写具体的开走方式
*/
public void go(){
System.out.println("平稳的开走了");
}
}

3、新建一个具体的BMW车-BMWm5

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
/**
* BMWm5系列汽车
* @author mapingsheng
*/
public class BMWm5 implements BMW{
BMWm5(){
System.out.println("--------BMWm5---------");
open();
start();
go();
System.out.println("--------BMWm5---------");
}
public void open(){
System.out.println("自动打开车门");
}
public void start(){
System.out.println("一键启动");
}
public void go(){
System.out.println("嗖的一下开走了");
}
}

4、建造BMW汽车工厂

我们在上面几步骤中已经初步知道我们制造的汽车的样子了,那下面就赶紧造一个汽车工厂开始生产吧。

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
/**
* 汽车工厂
* @author mapingsheng
*/
public class BMWFactory {
static BMW bmw;
/**
* 订单方法,根据用户选择的汽车型号生产对应型号的汽车
* @param type
* @return
*/
public static BMW orderBMW(String type){
return createBMW(type);
}
/**
* 生产方法,根据订单提供的型号生产对应的型号的汽车
* @param type
* @return
*/
private static BMW createBMW(String type){
if("x6".equals(type)){
bmw = new BMWx6();
}else if("m5".equals(type)){
bmw = new BMWm5();
}
return bmw;
}
}

上面的汽车工厂提供订单方法和生产方法,生产方法为私有,订单方法为共有,因为客户只需要通过订单系统下订单就行了,具体生产是不需要关注的。

5、测试

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 class Test {
public static void main(String[] args) {
/**
* 下一个型号为x6的汽车订单,然后下完订单后就获得了对应型号的汽车
*/
BMW x6 = BMWFactory.orderBMW("x6");
System.out.println("--------BMWx6---------");
x6.open();
x6.start();
x6.go();
/**
* 下一个型号为m6的汽车订单,然后下完订单后就获得了对应型号的汽车
*/
BMW m5 = BMWFactory.orderBMW("m5");
System.out.println("--------BMWm5---------");
m5.open();
m5.start();
m5.go();
}
}

以上代码运行结果:

——–BMWx6———

手动打开车门

拧钥匙启动

平稳的开走了

——–BMWm5———

自动打开车门

一键启动

嗖的一下开走了

6、小结

截止目前,通过上面的例子,我们已经实现了一个工厂方法的设计模式,说白了就是定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。

上面的简单的设计模式看似还可以,简单易懂,并且目前项目中也有地方用到,主要是因为他简单,把复杂的容易变化的部分封装起来,甚至抽象出来,对外提供不变化的部分;但是这个模式也有很多问题,比如下面几个问题:

  • 后期陆续会推出很多型号的汽车,那么每当有新型号的汽车推出时都需要更改工厂类中的生产方法(添加if分支)
  • 目前仅仅在德国慕尼黑开了一家工厂,主要在慕尼黑生产、销售汽车;下一步需要在中国也开一家工厂,并且中国客户也可以通过订单方法向中国的工厂下订单
  • 中国工厂生产出来的汽车需要跟德国工厂生成的汽车不太一样,比如排量标准、长度等等

上述描述的几个问题貌似通过上面的工厂方法无法很好的解决,需要另一个设计模式-抽象工厂设计模式去解决这些难题。