研磨设计模式之工厂方法模式-5?3.3? 平行的类层次结构(1)什么是平行的类层次结构呢???????? 简单点说,假如
研磨设计模式之工厂方法模式-5
?
3.3? 平行的类层次结构
(1)什么是平行的类层次结构呢?
??????? 简单点说,假如有两个类层次结构,其中一个类层次中的每个类在另一个类层次中都有一个对应的类的结构,就被称为平行的类层次结构。
??????? 举个例子来说,硬盘对象有很多种,如分成台式机硬盘和笔记本硬盘,在台式机硬盘的具体实现上面,又有希捷、西数等不同品牌的实现,同样在笔记本硬盘上,也有希捷、日立、IBM等不同品牌的实现;硬盘对象具有自己的行为,如硬盘能存储数据,也能从硬盘上获取数据,不同的硬盘对象对应的行为对象是不一样的,因为不同的硬盘对象,它的行为的实现方式是不一样的。如果把硬盘对象和硬盘对象的行为分开描述,那么就构成了如图10所示的结构:

?图10? 平行的类层次结构示意图
??????? 硬盘对象是一个类层次,硬盘的行为这边也是一个类层次,而且两个类层次中的类是对应的。台式机西捷硬盘对象就对应着硬盘行为里面的台式机西捷硬盘的行为;笔记本IBM硬盘就对应着笔记本IBM硬盘的行为,这就是一种典型的平行的类层次结构。
??????? 这种平行的类层次结构用来干什么呢?主要用来把一个类层次中的某些行为分离出来,让类层次中的类把原本属于自己的职责,委托给分离出来的类去实现,从而使得类层次本身变得更简单,更容易扩展和复用。
一般来讲,分离出去的这些类的行为,会对应着类层次结构来组织,从而形成一个新的类层次结构,相当于原来对象的行为的这么一个类层次结构,而这个层次结构和原来的类层次结构是存在对应关系的,因此被称为平行的类层次结构。
(2)工厂方法模式跟平行的类层次结构有何关系呢?
??????? 可以使用工厂方法模式来连接平行的类层次。
??????? 看上面的示例图10,在每个硬盘对象里面,都有一个工厂方法createHDOperate,通过这个工厂方法,客户端就可以获取一个跟硬盘对象相对应的行为对象。在硬盘对象的子类里面,会覆盖父类的工厂方法createHDOperate,以提供跟自身相对应的行为对象,从而自然的把两个平行的类层次连接起来使用。
3.4? 参数化工厂方法
??????? 所谓参数化工厂方法指的就是:通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象,这种情况就被称为参数化工厂方法。当然工厂方法创建的不同的产品必须是同一个Product类型的。
????????来改造前面的示例,现在有一个工厂方法来创建ExportFileApi这个产品的对象,但是ExportFileApi接口的具体实现很多,为了方便创建的选择,直接从客户端传入一个参数,这样在需要创建ExportFileApi对象的时候,就把这个参数传递给工厂方法,让工厂方法来实例化具体的ExportFileApi实现对象。
??????? 还是看看代码示例会比较清楚。
(1)先来看Product的接口,就是ExportFileApi接口,跟前面的示例没有任何变化,为了方便大家查看,这里重复一下,示例代码如下:?
?
?????? ?看完上述代码,会体会到简单工厂和工厂方法模式是有很大相似性的了吧,从某个角度来讲,可以认为简单工厂就是工厂方法模式的一种特例,因此它们的本质是类似的,也就不足为奇了。
2:对设计原则的体现
??????? 工厂方法模式很好的体现了“依赖倒置原则”。
??????? 依赖倒置原则告诉我们“要依赖抽象,不要依赖于具体类”,简单点说就是:不能让高层组件依赖于低层组件,而且不管高层组件还是低层组件,都应该依赖于抽象。
??????? 比如前面的示例,实现客户端请求操作的ExportOperate就是高层组件;而具体实现数据导出的对象就是低层组件,比如ExportTxtFile、ExportDB;而ExportFileApi接口就相当于是那个抽象。
??????? 对于ExportOperate来说,它不关心具体的实现方式,它只是“面向接口编程”;对于具体的实现来说,它只关心自己“如何实现接口”所要求的功能。
??????? 那么倒置的是什么呢?倒置的是这个接口的“所有权”。事实上,ExportFileApi接口中定义的功能,都是由高层组件ExportOperate来提出的要求,也就是说接口中的功能,是高层组件需要的功能。但是高层组件只是提出要求,并不关心如何实现,而低层组件,就是来真正实现高层组件所要求的接口功能的。因此看起来,低层实现的接口的所有权并不在底层组件手中,而是倒置到高层组件去了。
3:何时选用工厂方法模式
??????? 建议在如下情况中,选用工厂方法模式:
如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类去实现如果一个类本身就希望,由它的子类来创建所需的对象的时候,应该使用工厂方法模式
3.7? 相关模式 工厂方法模式和抽象工厂模式
??? 这两个模式可以组合使用,具体的放到抽象工厂模式中去讲。工厂方法模式和模板方法模式
??? 这两个模式外观类似,都是有一个抽象类,然后由子类来提供一些实现,但是工厂方法模式的子类专注的是创建产品对象,而模板方法模式的子类专注的是为固定的算法骨架提供某些步骤的实现。
??? 这两个模式可以组合使用,通常在模板方法模式里面,使用工厂方法来创建模板方法需要的对象。
?
工厂方法模式结束,谢谢观看!
另外这里有俩个疑问:1.关于文章中模式的一些概念的说明是大牛自己的理解,还是权威解释。
2."工厂方法模式的本质:延迟到子类来选择实现。"不是太明白。是写代码的时候延时到子类实现;还程序运行时延时到子类实现。
另外这里有俩个疑问:1.关于文章中模式的一些概念的说明是大牛自己的理解,还是权威解释。
2."工厂方法模式的本质:延迟到子类来选择实现。"不是太明白。是写代码的时候延时到子类实现;还程序运行时延时到子类实现。
谢谢夸奖,我的目的也是写出点东西来,不是搞很多花哨的故事,看起来轻松,看完就扔,因为没货;而是根据实践经验,除了经典的理论,再结合实际应用来阐述,帮助更多的朋友真正理解设计模式,并应用设计模式。
回答你的疑问:
“1.关于文章中模式的一些概念的说明是大牛自己的理解,还是权威解释。
”
答:概念说明大都来自GoF的设计模式一书,绝对正统,当然也还有一些个人的理解和经验总结。在回答另外一个朋友的问题的时候也说过,我没有看过其他的设计模式的书,十年来只看GoF的设计模式,GoF的书是所有讲设计模式的根源,大家都是从它那里来的。
“2."工厂方法模式的本质:延迟到子类来选择实现。"不是太明白。是写代码的时候延时到子类实现;还程序运行时延时到子类实现。”
答:两者都对,一个是开发期,一个是运行期。
11 楼 liuyupy 2010-07-12 该系列的讲解精彩细致,唯一不足之处就是示例不具备应用场景的代表性(或是没体现出来,当然可以通过想象创建需求),若是在最后部分其它框架相关应用 浅示 ,会让绕梁之味更浓.
论坛中讲模式的文章能 深广兼顾 新老皆懂 的,无出其二.再赞. 12 楼 jiangduxi 2010-07-14 lz对工厂方法模式的1-5篇讲解,让我学会很多。非常感谢楼主! 13 楼 chjavach 2010-07-16 liuyupy 写道该系列的讲解精彩细致,唯一不足之处就是示例不具备应用场景的代表性(或是没体现出来,当然可以通过想象创建需求),若是在最后部分其它框架相关应用 浅示 ,会让绕梁之味更浓.
论坛中讲模式的文章能 深广兼顾 新老皆懂 的,无出其二.再赞.
确实有这个遗憾,主要是目的在于深入的讲解模式本身,没有过多的涉及具体的业务,这里提业务也是做为讲解模式的载体而已,不过我想,任何事情都很难尽善尽美,要有所得或许就必有所弃,呵呵.
对于模式和具体的应用场景的关系,有机会再深入的写写,而且,一个模式可以在很多很多的应用场景出现,难免会出现挂一漏万的现象,也只能是当抛砖引玉了.
14 楼 as_you_2007 2010-08-25 讲解真的很透彻啊,期待博主关于抽象工厂模式的文章。 15 楼 icekzl 2010-09-07 关于 ioc和工厂方法的使用,一点点意见 不一定对。说出来大家讨论。
这个type参数 ,我一般会使用ioc容器中定义的 beanName/id 如spring ioc
protected ExportFileApi factoryMethod(String beanName){
ExportFileApi api =null;
try(){
api = (ExportFileApi)Ioc.getBean(beanName)
}catch(...){
...
}
return api;
}
}
要是又不要暴露出去 public static 再用,这里 只是关注以前 自己第一个感觉,用子类实现的地方 ,尽量不要使用if else 避免未来扩展使用的地方 if-else 泛滥。再就是 使用者 可以不必new 新子类使用,只是 传递数据依赖而已。比如:
public class Client {
public static void main(String[] args) {
//创建需要使用的Creator对象 %比如这里加入xml%
ExportOperate operate = new xmlExportOperate();
就可以不需要了。
可以一直使用
ExportOperate operate new =baseOperate()
//下面变换传入的参数来测试参数化工厂方法
operate.export(xml,"Test1");
operate.export(db,"Test2");
operate.export(txt,"Test3");
}
}
新的子类中 也不必 特意加入 if -else来处理选择了。
protected ExportFileApi factoryMethod(int type){
ExportFileApi api = null;
//可以全部覆盖,也可以选择自己感兴趣的覆盖,
//这里只想添加自己新的实现,其它的不管
if(type==3){
api = new ExportXml();
}else{
//其它的还是让父类来实现
api = super.factoryMethod(type);
}
return api;
}
引入新的实现子类 只要注册到ioc中,代码侵入几乎为零老的代码基本不需要过多掉正。因为 beanName 可能是 session数据也可能是db数据 ,本着 数据依赖为最小依赖的想法,觉得 工厂方法和ioc配合 有的时候还是可以用的。
听兄台讲解设计模式,感觉轻松愉快,非常支持。要是有出书,一定支持 希望有机会拿个签名版。。^_^(失业中的小弟)