CSDN话题挑战赛第2期
参赛话题:学习笔记
写在前面
作者简介:鲸海鹿林
博客主页:鲸海鹿林的主页
名言警句:keep calm and carry on
本系列参照HeadFirst系列设计模式这本书,换言之,是 HeadFirst设计模式这本书的读书笔记,让我们一起学习吧!
认识工厂模式(Factory Method Pattern)
new的思考
这里有一些要实例化的具体类,究竟实例化哪一个类,要在运行时由一些条件来决定。当看到这样的代码,一旦有变化或扩展,就必须重新打开这段代码进行检查和修改。通常这样修改过的代码将造成部分系统更难维护和更新,而且也很容易犯错。
识别变化的方面
假设你有一个比萨店,身为对象村内最先进的比萨店主人,你的代码可能这么写:
Pizza orderPizza(){
Pizza pizza = new Pizza();
pizza.prepare();
pizza.break();
pizza.cut();
pizza.box();
return pizza;
}
但是你需要更多的比萨类型……所以必须增加一些代码,来决定适合的比萨类型,然后再制造比萨:
Pizza orderPizza(String type) {
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.break();
pizza.cut();
pizza.box();
return pizza;
}
当需要增加更多的类型……
你发现你所有的竞争者都已经在他们的菜单中加入了一些流行风味的比萨:Clam(蛤蜊) Pizza、Veggie(素食) Pizza。很明显,你必须要赶上他们,所以也要把这些风味加进你的菜单中。而最近Greek Pizza卖的不好,所以你决定要将它从菜单中去掉:
Pizza orderPizza(String type) {
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
// }else if(type.equals("greek")){
// pizza = new GreekPizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}}else if(type.equals("clam")){
pizza = new ClamPizza();
}}else if(type.equals("veggie")){
pizza = new VeggiePizza();
}
pizza.prepare();
pizza.break();
pizza.cut();
pizza.box();
return pizza;
}
很明显,如果实例化某些具体类,将使orderPizza()出问题,而且也无法让orderPizza()对修改关闭;但是,现在我们已经知道那些会改变,哪些不会改变,该是封装出马的时候了。
还有一些细节有待补充,比方说,原本在orderPizza()方法中的创建代码,现在该怎么写?现在就来为比萨店实现一个简单的比萨工厂。
建立一个简单比萨工厂
先从工厂本身开始,我们要定义一个类,为所有比萨封装创建对象的代码。
public class SimplePizzaFactory{
Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}}else if(type.equals("clam")){
pizza = new ClamPizza();
}}else if(type.equals("veggie")){
pizza = new VeggiePizza();
}
return pizza;
}
}
重做PizzaStore类
是时候修改我们的客户代码了,我们所要做的是仰仗工厂来为我们创建比萨,要做如下的改变:
public class PizzaStore{
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
public Pizza orderPizza(String type){
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.break();
pizza.cut();
pizza.box();
return pizza;
}
}
定义简单工厂
再提醒一次:在设计模式中,所谓的“实现一个接口”并不一定表示写一个类,并利用implement关键词来实现某个Java接口。实现一个接口泛指实现某个超类型(可以是类或接口)的某个方法。
已有的做法……
如果利用SimplePizzaFactory,写出三种不同的工厂,分别是NYPizzaFactory、ChicagoPizzaFactory、CaliforniaPizzaFactory,那么各地加盟店都有适合的工厂可以使用,这是一种做法。
让我们看看未来会变成什么样……
但是你想要多一些质量控制……
在推广SimpleFactory时,你发现加盟店的确是采用你的工厂创建比萨,但是其他部分,却开始采用他们自创的流程:烘烤的做法有所差异、不要切片、使用其他厂商的盒子。
再想想这个问题,你真的希望能够建立一个框架,吧加盟店和创建比萨捆绑在一起的同时又保持一定的弹性。
在我们稍早的SimplePizzaFactory代码之前,制作比萨的代码绑在PizzaStore里,但这么做却没有弹性。那么,该如何做才能够吃掉比萨又保有比萨呢?
给比萨店使用的框架
有个做法可让比萨店制作活动局限于PizzaStore类,而同时又能让这些加盟店依然可以自由地制作该区域的风味。
所要做的事情,就是把createPizza()方法放回到PizzaStore中,不过要把它设置成“抽象方法”,然后为每个区域风味创建一个PizzaStore的子类。
允许子类做决定
别忘了,PizzaStore已经有一个不错的订单系统,由orderPizza()方法负责处理订单,而你希望所有加盟店对于订单的处理都能够一致。
各个区域比萨店之间的差异在于他们制作比萨的风味,我们现在要让createPizza()能够应对这些变化来负责创建正确种类的比萨。做法是让PizzaStore的各个子类负责定义自己的createPizza()方法。所以我们会得到一些PizzaStore具体的子类,每个子类都有自己的比萨变体,而仍然适合PizzaStore框架,并使用调试好的orderPizza()方法。
当orderPizza()调用createPizza()时,某个比萨店子类将负责创建比萨。做哪一种比萨?当然是由具体的比萨店来决定。
那么,子类是实时做出这样的决定吗?不是,但从orderPizza()的角度来看,如果选择在NYStylePizzaStore订购比萨,就有这个子类决定。严格来说,并非由这个子类实际做决定,而是由顾客决定到哪一家风味的比萨店才决定了比萨的风味。
订购一个比萨
认识工厂模式(Factory Method Pattern)
工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
另一个观点:平行的类层级
我们已经看到,将一个orderPizza()方法和一个工厂方法联合起来,就可以成为一个框架。除此之外,工厂方法将生产知识封装进各个创建者,这样的做法,也可以被视为是一个框架。
定义工厂方法模式
对象依赖
依赖倒置原则
原则的应用
疑惑——为什么叫依赖倒置?
倒置你的思考方式
指导方针——帮助你遵循该原则
回到比萨店……
建造原料工厂
创建纽约原料工厂
重做比萨
再回到比萨店
我们做了什么?
订购更多的比萨