前些日子把GoF的设计模式看完了,不过有些地方理解的还不是很深入,而且有段时间不看也有些忘记。当时感触颇深,读后就只有仰止的份:真不愧是大家手笔,把OO真正的精髓都体现在了这薄薄的一本书中了。
这本书的完整的书名是“Design Patterns - Elements of Reusable Object-Oriented Software”,注意这里的reusable这个词,“软件复用”的重要性我想应该不用我再多啰嗦了,它是高效率、高质量、高可用的软件的基础。从软件设计的混沌时期到结构化、模块化程序设计,面向过程的程序设计范型,到面向对象OO程序设计范型,到泛型编程GP,到设计模式,到基于组件的编程,到中间件……“软件复用”一直是程序员不断追求的目标。“复用”是分为不同的层次的,我们在C中用到的库函数实际上是结构化程序设计范型中的源码级别的复用;Java, .Net, Delphi中使用的类库中等可以说OO里面类层次上 的、功能性的复用;组件复用实际上是一个设计良好的低耦合(仅仅通过对外提供的一些接口)、高内聚的小型软件系统的复用;中间件和组件类似,但复用的层次更高;而设计模式则是系统架构、OOD思想层次上的复用。
衡量某种类型的复用的好坏的标准应该是这种复用能够解决的问题的多少。某种类型的复用的级别越高,就意味着它能解决的问题越多。个人觉得,思想的复用才是最高级别的复用,掌握了某种思想能解决的问题才是最多的。比如算法“归并排序”这个具体的算法:根据这个算法,我们能够在O(n*log(n))的时间复杂度上解决所有的排序问题,这确实已经是个很不错的复用了。但也仅仅限于排序问题,出了这个领域,本算法也就没有用武之地了。这是一个比较底层次上的复用。而如果我们掌握了归并排序的设计思想--分治法的思想,那我们能解决的问题就要多得多得多了:快速排序、二分检索、求最大最小元素、选择问题、矩阵乘法,甚至不限于计算机科学的其他领域的问题。。。这就是更高一个层次的复用了。
大家可能会有这样的困惑,学了C++,学了Java这些面向对象的程序设计语言,也基本搞清楚了OO的3大特性:封装、继承、多态,但却不知道这些语言层面的特性应该怎么去用。C++中的虚函数、纯虚函数、虚函数表的实现机制、多继承、虚继承、对象构造的顺序可以搞的很透彻、很清楚,(当然Java, .Net中也有类似概念,这里不再一一列举 )却不知道在软件设计中怎么使用。怎么把问题从现实的域中映射到计算机中?什么时候需要定义一个类?各个对象之间应该怎样交互?什么时候把一个成员函数定义为virtual? 设计模式会告诉我们答案。
因此,可以说,不懂设计模式的话就不能说真正理解OO。 设计模式这种设计思想层次上的复用,是高级软件工程师、系统架构师的必修课。
今日突发奇想,如果每个模式看完之后都把自己的理解、思考用通俗易懂的语言写出来,既能够加深自己的理解,又可以让一个初学者能够在最短的时间内掌握某个模式的精髓,一举两得,岂不快哉?
本文内容纯属一家之言,旨在根据自己的理解帮助初学设计模式者入门,兼抛砖引玉。在下才疏学浅,如有不对的地方,还请赐教。我的邮箱为(a*4)lex[at]21cn.com (//"(a*4)"请替换成4个a,谢谢)
第一站,创建型模式之工厂方法(Factory Method)。什么是工厂方法呢?首先,大家得对这个模式有个基本的了解。工厂是做什么的?就是生产产品(product)吧。如果我们调用它的constructor,product = new Product(),这相当于是“自给自足”。如果有个叫Creator的类,我们通过调用它的某个方法FactoryMethod()来得到实际的product,那么这个方法就叫做工厂方法。说白了,就是我们不去直接调用Product的constructor(有时为了强制必须通过工厂方法来生成对象,甚至把Product类的所有版本的constructor都设置成非public的,这样的情况下我们就根本无法直接使用new来“自给自足”了)。
根据GoF的《设计模式》书中的介绍,我们先来看看工厂方法比较权威的定义。
名 称
Factory Method(工厂模式)
意 图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
适 用 环 境
当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
结 构

光看这个解释和类图,感觉很晦涩,举个例子吧。
假如我们要写个类似MS Office这样的软件,同时提供Word文字处理和Excel电子表格的功能。
那么利用工厂方法模式,我们可以得到下面的类图所示的结构。

提示:这是个标准的UML中的类图,注意名称用斜体字书写的类或者方法都是抽象的。抽象类不能被实例化,只能用来引用某个具体子类的实例;而抽象方法就是会表现出“多态性”的方法,也就是说会非调用该实例所属于的具体子类中相应版本的非抽象方法。 图中那个docs表示OfficeApp中包含有一个Document对象的集合 docs,就象实际中的一个程序可以打开多个文档那样。
具体的代码我就不写了,就就着类图来说明吧:在OfficeApp中有个非抽象(考虑这里为什么是”非抽象“的方法?当然也可以把它定义成一个抽象的方法,然后把相同的逻辑在每个子类中相应的非抽象方法中都重复地粘贴一遍,但这样的代码不但冗余难于维护,而且还会造成潜在的不一致性)的方法NewDocument,我们需要在该方法中创建一个具体的Document对象(或者是ExcelDoc或者是WordDoc),以及一些后续操作,比如象代码所示的那样,把创建的这个新文档添加到当前的所有文档列表docs中去(docs.Add(doc)),然后再打开当前的文档(doc.Open()。但这里问题出来了,我们在此时并不知道究竟应该创建那个具体的子类(究竟是WordDoc呢还是ExcelDoc呢?),我们唯一知道的只有它们共同的那个不能被实例化的抽象父类Document。
这里就是“多态“大显身手的地方了,我们在WordApp中定义了一个抽象方法CreateDocument(返回一个Document的指针或者引用),而在WordApp和ExcelApp中相应版本的具体的CreateDocument方法中,则分别访问WordDoc和ExcelDoc的constructor来实现具体的Document的子类的创建,分别返回一个WordDoc或者ExcelDoc的指针或引用。
好了,假设我们创建了一个具体的WordApp对象myWordApp,那么当用户点击“新建”菜单的时候,我们的处理就只需要简单地调用一下myWordApp.NewDocument()就行了,这个函数是从它的基类OfficeApp中继承下来的,但会根据“多态性”创建一个AppDoc类型的具体类型。
这里,我们说CreateDocument()就是一个工厂方法。它定义一个用于创建对象(Document)的接口,让子类(WordApp或者ExcelApp)决定实例化哪一个类(WordDoc或者ExcelDoc)。CreateDocument()这个工厂方法使一个类(Document的子类:WordDoc或者ExcelDoc)的实例化延迟到其子类(OfficeApp的子类:WordApp或者ExcelApp)。
从这个例子中,我们把工厂方法模式抽象到一般情况,就得到最前面的那张类图。
这张图和上面的那个例子中的例图是很容易对应起来的,只不过这里省略了其他一些无关紧要的、与本模式无关的成员方法。
工厂方法还有一种用法,有时候并不需要在Creator这边实现多级的继承关系,只需要一个具体的Creator类就行了,其中的工厂方法根据某个判定依据分别生成不同的ConcreteProduct并返回其指针或者引用。
版权声明:本文版权所有者为(a*4)lex[at]21cn.com 。遵循GPL License,欢迎自由传播。请尊重他人的劳动成果,转载请注明出处,谢谢!
本文介绍了设计模式中的工厂方法模式,详细阐述了其原理及应用案例。通过对比不同复用层级,强调了思想复用的重要性,并以MS Office为例,展示了如何使用工厂方法模式创建不同类型的产品。
9197

被折叠的 条评论
为什么被折叠?



