创建型设计模式:简单工厂(simple factory)
假设你在开发一个商品发货系统,公司没有物流运输能力的,需要对接快递公司。刚开始,货物是交给一个快递公司,你的发货流程简单描述为下单,收货,货物运送。代码类似于:
public void send(Goods goods) {
Expess express = new Express();
express.placeOrder(goods);
express.pickup();
express.delivery();
}
此时快递Express
对象是直接通过new
构建。
合作的快递公司增多
后来公司增加了顺丰,邮政作为货物的快递公司,因为给快递公司的发货流程不变,还是下单,收货和货物运送。所以抽象了Express,新增ShunFengExpress和ChinaPostExpress继承于Express。发货代码就改为:
public void send(String expressType, Goods goods) {
Express express;
switch(expressType) {
case "SHUNFEN":
express = new ShunFengExpress();
case "CHINA_POST":
express = new ChinaPostExpress();
default:
express = new ShunFengExpress();
}
express.placeOrder(goods);
express.pickup();
express.delivery();
}
send()
方法增加了expressType参数,用来决定使用哪家快递公司来发货。
问题:合作的快递公司名单不时变更
随着业务的开展,公司合作的快递公司有时要发生变更,如增加韵达,去掉邮政等。如果要满足这个需求的话,我们就需要不断修改send()
方法里的代码:
public void send(String expressType, Goods goods) {
Express express;
switch(expressType) {
case "SHUNFEN":
express = new ShunFengExpress();
case "YUNDA":
express = new YunDaExpress();
case "ZHONG_TONG":
express = new ZhongTongaExpress();
default:
express = new ShunFengExpress();
}
express.placeOrder(goods);
express.pickup();
express.delivery();
}
对上面的代码review,你会发现发货流程(下单,收货和送货)本身是没有任何变化的,send()方法中,唯一发生变化的是创建快递公司的部分,而且是随着业务变化要不时修改。
这违背了“开闭原则”!开闭原则要求我们对扩展开发,对修改关闭。
简单工厂:封装会发生变化的创建对象代码
现在你知道会变化的部分是创建Express对象的代码。把创建代码抽离出来,封装在一个新的类中,这个类我们称为简单工厂。SimpleExpressFactory就负责Express对象的创建,代码类似于:
public class SimpleExpressFactory {
public Express createExpress(String expressType) {
Express express;
switch(expressType) {
case "SHUNFEN":
express = new ShunFengExpress();
case "YUNDA":
express = new YunDaExpress();
case "ZHONG_TONG":
express = new ZhongTongaExpress();
default:
express = new ShunFengExpress();
}
return express;
}
}
SimpleExpressFactory的createExpress
方法接受expressType作为创建Express对象的参数。新的发货系统修改为:
public GoodsSender {
SimpleExpressFactory expressFactory;
public GoodsSender(SimpleExpressFactory expressFactory) {
this.expressFactory = expressFactory;
}
public void send(String expressType, Goods goods) {
Express express = expressFactory.createExpress(expressType);
express.placeOrder(goods);
express.pickup();
express.delivery();
}
}
疑问
你会发现创建代码只是从一个类迁移到了另一个类,如果业务变化,还是要修改SimpleExpressFactory里的代码。不时修改的问题仍然存在。那这样做有什么优势呢?
抽离封装创建代码的好处:
- 将对象的创建与对象的使用过程分离,调用者只需要调用创建方法获取对象即可,不需要关心创建对象过程
- 如果有多个地方需要获取对象,简单工厂就可以被复用。如采购系统要查下各个快递的价格等。
- 把通过new创建具体实例的代码移除,这样GoodsSender就不依赖于Express的具体实例。
简单工厂的变种:静态工厂方法
简单工厂方法往往只需要一个创建的方法,一个常见的技巧是,把简单工厂定义为静态方法,这种做法称为静态工厂方法。代码就类似于:
public GoodsSender {
public void send(String expressType, Goods goods) {
Express express = GoodsSender.createExpress(expressType);
express.placeOrder(goods);
express.pickup();
express.delivery();
}
public static Express createExpress(String expressType) {
Express express;
switch(expressType) {
case "SHUNFEN":
express = new ShunFengExpress();
case "YUNDA":
express = new YunDaExpress();
case "ZHONG_TONG":
express = new ZhongTongaExpress();
default:
express = new ShunFengExpress();
}
return express;
}
}
定义为静态工厂方法有个缺点:它不能通过类的继承方式,改变创建对象的行为。