设计模式是进阶高级开发的必经之路。
工厂方法模式(Factory Method Pattern)是《Head First 设计模式》介绍的第四个模式,只要你认真读完本篇博文,一定能很好地掌握此模式。
定义
工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的
分析
程序要运行,需要创建各种对象。而我们之前为了松耦合、高可用做的努力,最后在main方法里,都需要实例化具体的类对象才行,比如:
Noodles noodle= new BeefNoodles();//实例化牛肉面
Beverage beverage = new MilkyTea();//原味奶茶
- 1
- 2
我们之前提倡的原则:面向接口编程,不面向实现编程。可一旦new 具体对象,程序立马有了瑕疵。假设需要实例化多种子类对象,调用方法里就要写各种new,调用类里就需要引入所有依赖的子类:
import BeefNoodles;//引入所有以来的实现类型
import DuckLegNoodles;
public order(){
Noodles noodle = createNoodle("牛肉面");
noodle.cost();//计算价格
noodle.send();//上菜
}
public createNoodle(面类型){
Noodles noodle;
//写在客户端代码里的实例化
if(牛肉面){
noodle = new BeefNoodles();//牛肉面一份
}else (鸭腿面){
noodle = new DuckLegNoodles();//鸭腿面一份
}
}
noodle.doSomething();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
若是以后需要的对象类型发生变化,此段代码必定要频繁修改,造成隐患。根据第一原则:把变化的部分抽取、封装。客户端的逻辑可以简化为:获取对象——对象做些事情。我们把获取对象的逻辑交给一个专门负责此类事情的高手去做:简单工厂SimpleFactory。
老将出马,一个顶俩
public class SimpleFactory{
public Noodles getNoodles(参数){//可以为static方法
//创建对象的逻辑让SimpleFactory具体负责。
Noodles noodle;
if(牛肉面){
noodle = new BeefNoodles();//牛肉面一份
}else (鸭腿面){
noodle = new DuckLegNoodles();//鸭腿面一份
}
return noodle;
}
}
//客户端调用
Noodles noodle = SimpleFactory.getNoodles(参数);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
目前来看,我们只是把代码换了个位置,然后从客户端调用,并没有特别出彩的设计。不过有一点:客户端已经不需要依赖各种具体实现了,因为不管什么面都是面(超类型),客户端获得引用直接调用即可。但是,如果现在新增一个店也需要卖面条。按照原来的方式,它需要写一段实例化面条的代码,并具有同样的风险。现在,有了老司机:SimpleFactory,它只需将它的需求告诉简单工厂,自然就能获得自己想要的对象。这样,以后不管多个客户端,都可以复用简单工厂。
这种把创建对象的逻辑封装起来并提供简单的对外接口,称为简单工厂模式(其实算不上一个模式,毕竟实在很简单)。
客户太多,老将扛不住
快乐的时光总是短暂的。
面条太好吃,大家想开加盟店,开到北京上海,天津广州云南去。中国幅员辽阔,各地的口味不尽相同,为了适应当地风情,需要多款面条产品,兰州牛肉拉面,北京牛肉擀面,天津牛肉炒面。。。(有没有饿),大家向SimpleFactory请求拉面,传入品种标识,SimpleFactory里的getNoodles()方法不断增长:
public Noodles getNoodles(参数){//可以为static方法
//创建对象的逻辑让SimpleFactory具体负责。
Noodles noodle;
if(牛肉面){
noodle = new BeefNoodles();//牛肉面一份
}else if(鸭腿面){
noodle = new DuckLegNoodles();//鸭腿面一份
}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}....
return noodle;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
这肯定不是个好办法,毕竟一个工厂产能有限,要开发生产太多品类,也不利用积累,分散了精力。只能多开几个工厂,把加盟店划分区域,一个区域一个工厂,负责此区域内加盟店的面条需求。
//客户端调用
Noodles noodle = BeijingFactory.getNoodles(参数);//北京工厂
Noodles noodle = LanzhouFactory.getNoodles(参数);//兰州工厂
...
- 1
- 2
- 3
- 4
本着抽象、封装的原则,我们可以把SimpleFactory抽象成接口,因为工厂都是负责做面条而已。只不过不同工厂做的步骤、原料不一样(实现不同,但上层并不关心)
public interface NoodlesFactory{
Noodles getNoodles(参数);
}
class BeijingFactory implements NoodlesFactory{
public Noodles getNoodles(参数){
Noodles noodle;
//根据参数,专门生产北京地区风味的面条
return noodle;
}
}
class LanzhouFactory implements NoodlesFactory{
public Noodles getNoodles(参数){
Noodles noodle;
//根据参数,专门生产兰州地区面条
return noodle;
}
}
//客户端:
持有一个面条工厂引用:
class BeijingStory{
NoodlesFactory noodlesFactory;
public BeijingStory(){
noodlesFactory = new BeijingFactory();
}
//下单
public order(){
//运行时才能知道是哪个工厂来生产自己需要的面条
Noodles noodle = noodlesFactory.getNoodles("牛肉面");
noodle.cost();//计算价格
noodle.send();//上菜
}
}
- 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
兰州、广州店同样如此,找一个能做本地口味面条的工厂(实现NoodlesFactory接口),就能继续用订单系统,不用改动太多。此时把简单工厂升级,变成了工厂方法模式。可以看到,工厂方法模式是在简单工厂上的进一步抽象、封装。让下层有更大、更多的实现自由,提高设计弹性,保留复用(工厂可以供应好几个门店)。
升级——抽象工厂模式
简单工厂方法——>工厂方法模式——>抽象工厂模式 是一套顶尖功法,不用自宫,也可成功。
本着诚信经营、真材实料的原则,所有门店生意都很好。但世界永远在变,不可能永远只卖面条,别人想吃黄焖鸡、瓦罐煨汤等国家著名连锁小吃的时候,就不来店里去别家了。为了解决这个问题,门店需要增加商品种类。按照之前的工厂方法模式,我们可以新增黄焖鸡工厂YellowChickFactory
和瓦罐煨汤工厂CrockSoupFactory
:
public interface YellowChickFactory{
YellowChick getYellowChick(String size);
}
class BeijingYellowChickFactory{
public YellowChick getYellowChick(String size){
//生产北京地区的黄焖鸡,大份、中份、小份等
return new BeijingYellowChick(size);
}
}
//CrockSoupFactory 代码略...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
这样设计,遵循了我们之前的原则,带来一定的好处。不过也有不足之处:毕竟面条不是时时刻刻都需要,在有黄焖鸡订单时,黄焖鸡工厂生产,意味着面条工厂就处停工状态(综合来说是这样,毕竟总客户资源某种程度上看是固定的),而面条工厂开工,则黄焖鸡工厂停工。
两个工厂的生产效率极大浪费。此时我们想到,能不能把连个工厂结合起来,即能生产面条又能做黄焖鸡,甚至瓦罐汤?
当然可以,我们可以抽象一个总的工厂接口即抽象工厂FoodFactory,里面有生产面条、黄焖鸡、瓦罐汤和其他商品的接口。各地区的工厂只需要实现此接口,实现自己的生产流程,就能满足所有的需求了:
public interface FoodFactory{
Noodles getNoodles(参数);
YellowChick getYellowChick(String size);
//瓦罐汤和其他类似,略
}
class BeijingFactory implements FoodFactory{
public Noodles getNoodles(参数){
Noodles noodle;
///...
//根据参数,专门生产北京地区面条
return noodle;
}
public YellowChick getYellowChick(String size){
//生产北京地区的黄焖鸡,大份、中份、小份等
return new BeijingYellowChick(size);
}
}
//兰州、广州的工厂同样如此,只不过具体的实现逻辑不通
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
门店调用:
//北京门店:
持有一个抽象工厂引用:
class BeijingStory{
FoodFactory foodFactory;
public BeijingStory(){
foodFactory= new BeijingFactory();
}
//面条下单
public noodlesOrder(){
Noodles noodle = foodFactory.getNoodles("牛肉面");
...
}
//黄焖鸡下单
public noodlesOrder(){
YellowChick chick= getYellowChick.getNoodles("小份");
...
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
此时:我们在工厂方法模式的基础上,合并多个工厂,并抽象出一个抽象工厂。此抽象工厂包含了一组生产对象的方法,子类实现此接口,来满足系统的调用。此模式就是:抽象工厂模式