关闭

设计模式应用实例(序言)

6352人阅读 评论(5) 收藏 举报

                                                 序言

Google

    我从上大学的时候开始编程,不过直到2000年左右才开始接触设计模式,当时已经毕业并在一家软件公司工作了一年多的时间,无论是编些代码还是开发大型的商业软件都算有了一些经验了,但是记得当时对设计模式的理解就是两个字:不懂。我不明白的地方当然很多,现在记得印象最深的就是“不懂”为什么要把很简单的东西搞得那么复杂。后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精髓所在,我所理解的“简单”就是一把钥匙开一把锁的模式,目的仅仅是着眼于解决现在的问题,而设计模式的“复杂”就在于它是要构造一个“万能钥匙”,目的是提出一种对所有锁的开锁方案。在真正理解设计模式之前我一直在编写“简单”的代码,这个“简单”不是功能的简单,而是设计的简单。简单的设计意味着缺少灵活性,代码很钢硬,只在这个项目里有用,拿到其它的项目中就是垃圾,我将其称之为“一次性代码”。在经过多年的意气风发,编写了大量“实用”的代码之后,我不得不一次次看到我的代码被“使用以后抛弃”,这使我变得痛苦不堪。每当我要开始编写一个新程序或开发一个新项目时,我会习惯性地把以前的代码拿出来看看,找找有没有可以直接在我的新项目中使用的东西,结果常常令我失望,除了一些功能性的代码和编程技巧可以借鉴之外,其它的东西必须经过修改才能使用。我就这样“简单”地修改,使其能够在新项目中使用,然后再抛弃,再修改,再抛弃,直到一个同名的类有了多得我自己都记不清楚的修改版本的时候,我知道不能再继续下去了,必须放弃编写“简单代码”,于是我重新开始学习设计模式。

    2001年的时候我开始为我的共享软件“Winmsg消息精灵”开发新的4.0版,在吸取了以前的教训之后,我开始在新版本的开发中有意识地引入设计模式,之所以这样做并不是为了哗众取宠,而是真的“痛定思痛”后的“改过自新”。使用设计模式为我带来了极大的好处,我不再一味地重写以前地代码,而是尽量使用“适配器模式(Adapter Pattern)”和“外观模式(Facade Pattern)”重用它们,这样减少了代码编写量,避免了大量的“体力劳动”。在设计4.0版的插件系统时,我引入了“抽象工厂模式(Abstract Factory Pattern)”、“桥模式(Bridge Pattern)”、访问者模式(Visitor Pattern)和“迭代器模式(Iteator Pattern)”,这样看起来使系统变得很复杂,但是在随后的版本升级中体现了它们的价值。我的插件模式从最初的简单数据处理功能到最后支持用户界面,发生了巨大的改变,但是整个插件系统只经过了很小的改动就很好地适应了它们,如果是使用以前的简单设计,恐怕又要重写全部代码了。使用设计模式不仅提高了代码的可扩展性,还提高了代码的重用性,我在开发“消息精灵”的一个插件时设计了一套加密算法支持系统,使用了“工厂模式(Factory Pattern)”、“单件模式(Singleton Pattern)”和“模板模式(Template Pattern)”,这套系统后来应用到了多个软件项目中,直到最近开发一个基于HTTP协议的远程控制软件时,我依然没有经过任何修改就重用了这套系统。

    很多我所认识的程序员在接触到设计模式之后,都有一种相见恨晚的感觉,有人形容学习了设计模式之后感觉自己好像已经脱胎换骨,达到了新的境界,还有人甚至把是否了解设计模式作为程序员划分水平的标准。但是设计模式确实不容易掌握,目前网上有很多关于设计模式的资料,也出版了很多关于设计模式的书,但都是针对模式的原理的介绍,或者说是对“GOF Book”的详细讲解手册,少量的代码也仅仅是为了说明原理而设计的,对设计模式有心的初学者看到这些资料后很难对“如何使用设计模式指导实现代码”产生感性上的认识,就更谈不上深入地理性认识设计模式了。其实学习设计模式并不在于死记硬背每种模式(即使是同一种模式,最终的实现结果也是五花八门),重点是如何在实际软件开发中使用这些模式。使用的过程是循序渐进的,开始是初级的使用(可能还包含理解的错误,比如使用与上下文不匹配的模式),然后在使用的过程中加深对模式的理解,能够灵活并正确地使用模式,并最终融会贯通,甚至创造出新的模式。

    我本人在学习和尝试使用设计模式的过程中,遇到了很多问题,也积累了一些经验,我在这里将其总结出来,抛砖引玉,希望对大家能够有所帮助。以下是我在使用设计模式过程中的一些模式应用实例,是这几年经验的一些总结,体现了我对设计模式的一些理解,每个实例都有对应的代码例子,这些例子都是从我的软件中摘录出来的,为了重点体现模式的核心而做了一定的简化。希望本文以及附带的实例代码,能够帮助大家理解设计模式,为大家建立一座从抽象的模式到具体实现代码之间的桥梁。由于认识有限,错误之处在所难免,欢迎拍砖。

    以下是本文的撰写计划,我会在工作之余慢慢写完它们,大家有什么意见可以随时反馈给我:
inte2000@163.com 或者 simon@winmsg.com

[附录:《设计模式应用实例》撰写计划]

                            设计模式C++使用实例

第一章  引言
1.1 什么是设计模式
1.2 为什么要使用设计模式
1.3 怎样使用设计模式
1.4 使用设计模式的几个原则


第二章  “适配器模式(Adapter Pattern)”应用实例
    在我的个人计算机信息管理软件“SysAdmin”中提供了用户文件校验的功能,以前的代码已经对CRC32校验封装了CCrc32Check类,现在需要支持另一种校验方法adler32,现在手上已经有了一套adler32校验算法的代码,只不过是以API的封装形式提供的,怎样将它们整合到原有的系统中呢?当然是使用适配器模式了。


第三章  “外观模式(Facade Pattern)”应用实例
    “Tabbars PlusIn for Visual C++ 6.0”是我发布的一款公开源码的自由软件,这个软件的功能之一就是提供了整个VC工作区(Workspace)代码打包压缩和从压缩文件中直接打开VC工作区查看代码的功能,其中文件压缩使用了Zlib库,Zlib库是一套API函数库,不尽提供了数据压缩的低级函数,还提供了直接对文件压缩的高级函数,甚至还提供了对gzip文件的操作函数,“Tabbars”只使用了这个库的很少功能,因此创建了CZipFile和CUnzipFile两个类专门操作这些函数,这样上层的功能实现模块就不需要了解整个Zlib库,只需使用这两个类就行了,这其实就是一个外观模式的应用实例。


第四章  “单件模式(Singleton Pattern)”应用实例
    Singleton模式通常是与其它模式结合使用的,在我本人的共享软件“Winmsg消息精灵”中使用的数据加密部分的设计应用了“Abstract Factory”模式,AbstractFactory负责创建加密算子对象,但是需要一套机制来保证为使用者提供合适的ConcreteFactory,使用Singleton模式再合适不过了,这一章就介绍这样一个Singleton模式,同时就在C++中使用Singleton模式模式进行了简单的探讨(涉及友员函数和对象的创建删除操作)。


第五章  “抽象工厂模式(Abstract Factory Pattern)”应用实例
    共享软件“Winmsg消息精灵”中的很多类都需要使用加密算子对象对数据进行加密,但是根据用户界面的选择或加密强度的要求,这些类可能需要根据实际情况决定使用哪一种加密算子,所以软件的数据加密部分的设计应用了“Abstract Factory”模式。


第六章  “生成器模式(Builder Pattern)”应用实例
    在基于HTTP协议的远程管理软件HttpAdmin中,Controller和Agent之间要传递各种不同的消息,其中之一就是Controller向Provider发送的控制命令,Controller将控制命令统一发送给Agnet,Agent再将这些命令分派给已经注册到Agent的某个Provider,命令的种类很多,但是在Controller和Agent之间传送的命令数据都由三部分组成,分别是命令的协议头、命令参数和校验尾部,每种命令的三个部分组成各不相同,我们设计使用生成器模式,每个生成器负责生成一种命令的协议头、参数段和校验尾部,并最终组合成一个完整的命令信息数据。


第七章  “访问者模式(Visitor Pattern)”应用实例
    在基于HTTP协议的远程管理软件HttpAdmin的设计中,Controller和Provider之间的数据传输有多种类型,比如文件上传类型、文件下载类型、流数据上传类型、流数据下载类型等等,它们分别对应一个传输类型类,这些传输类型类都维护者一些类似的信息,比如上传字节数,下载字节数,传输时间等等。在很多情况下需要统计这些信息,比如在界面上现实当前的数据传输情况等等。统计这些信息可以有很多种方式,最简单的就是通过这些类提供的属性访问函数一个一个计算,需要求和的就求和,需要计算平均值的就计算平均值。但是这样一来代码维护起来就比较困难了,每当统计方式发生改变或添加新的统计信息时,就需要查找所有的统计点,修改统计点处的统计代码,扩展性和复用性差。有更好的设计方式吗,当然有,那就是访问者模式,本章就结合这个例子介绍如何使用访问者模式。


第八章  “桥模式(Bridge Pattern)”应用实例
    在基于HTTP协议的远程管理软件HttpAdmin的设计中,Controller需要向某个Provider发送请求命令,并通过Provider的相应完成一个命令的处理。这些命令有很多种,但是每种命令都要使用一种传输类型来发送和接收数据,命令和传输类型都有相应的封装类(传输类型的封装类在第七章已经介绍了)。命令类型各种各样,并且它们的处理方式也是大相径庭,所以命令封装类是一个变数,同时,传输类型也是一个变数,因为传输类型也有很多种,那么如何在命令封装类和传输类型封装类这两个变数之间建立联系呢,使用桥模式就是最好的选择。


第九章  “装饰模式(Decorator Pattern)”应用实例
    使用License文件是软件加密的一种方式,我们的个人计算机信息管理软件“SysAdmin”使用CLicenseFile类处理对License文件的检查,处理过程中需要对文件进行CRC校验,RSA数字签名和统计MD5摘要,这个小系统使用了Decorator模式。


第十章  “组成模式(Compositor Pattern)”应用实例
    结合Windows的树控件介绍怎样将Compositor模式应用到一个操作递归结构数据类型的程序中。结合“基于HTTP协议的远程管理软件HttpAdmin”中管理远程文件目录信息的代码,介绍了一个Compositor模式的应用实例。


第十一章  “观察者模式(Observer Pattern)”应用实例
    “基于HTTP协议的远程管理软件”采用的是基于“Controller - Agent - Provider”的三层体系架构,其中“Controller - Agent”和“Provider - Agent”都是m:1的关系,多个Controller可以登陆到一个Agent上,同样,多个Provider也可以注册到一个Agent上,Agent负责处理所有的事件,比如新Provider注册到Agent或Provider从Agent去注册等等,这是一个典型的Observer模式,Controller向Agent订阅(Subscribe)这些事件。


第十二章  “备忘录模式(Memento Pattern)”应用实例
    在HttpAdmin中,Controller一次可能向Agent提交多个命令,如果因为网络或其它原因导致Provider不能完成这些命令,Controller就负责将这些命令信息(主要是命令参数和当前命令的执行状态)保存到磁盘文件上,等Controller下次启动时加载这个信息文件,使得所有的命令能够恢复到上次的状态,由用户选择是否继续执行这些命令。这样的设计涉及到对CControlCommand类的信息的访问,但是CControlCommand类的一些参数信息和状态信息是内部私有的,如果提供对这些参数的访问方法将破坏CControlCommand类的封装性。既要能够保存和恢复CControlCommand类的状态,又不能对CControlCommand类的设计做太大的改变,以至于要以破坏数据封装为代价,那么使用备忘录模式就是最好的选择了,本章将结合上面提到的例子介绍一个备忘录模式的应用实例。


第十三章   “模板模式(Template Pattern)”使用实例
    个人计算机信息管理软件“SysAdmin”采用支持插件的架构体系,每种插件都有一个相应的CxxxPlusIn与之对应,其基类是CPlusIn类,由于CPlusIn类的很多方法都是抽象方法,它们的实现需要延迟到相应的CxxxPlusIn类实现,所以CPlusIn类的设计采用了模板模式,本章将介绍模板模式的应用实例。


第十四章   “命令模式(Command Pattern)”使用实例
    在HttpAdmin的设计中,Controller向Agent提交命令是通过用户的界面操作实现的,用户使用界面上菜单、工具栏或命令行输入接口输入一个命令以及命令需要的参数,然后Controller负责触发相应的命令处理类完成命令的处理过程。从接收命令参数到触发相应的命令类处理过程,我们设计使用了命令模式。使用命令模式,不尽省去了使用回调函数的烦恼,还使得系统具有了更好的可扩展性。比如,我们的系统就可以利用Windows的多线程机制加快命令执行的过程,将下载一个文件夹的命令(Command)委托给若干个下载单个文件的命令(Command)。


第十五章   “迭代器模式(Iteator Pattern)”使用实例
    第十四章介绍的命令模式实例中,为了使每个命令(Command)不尽能够触发单个命令处理过程,还支持同时触发多个命令处理过程(顺序或并行),我们将每个Command设计成多个Children Commands的组合,CCommandList类被设计用来维护这些Children Commands,这有点类的组合模式,但是组合模式更强调各个组合节点之间的关系,而Command关心的仅仅是有没有Children Commands的问题(通常至少有一个)。在这个设计中,为了能够方便地访问CCommandList,迭代器模式便被引用进来。本章就来介绍一个迭代器模式的实例。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1470705次
    • 积分:14284
    • 等级:
    • 排名:第889名
    • 原创:104篇
    • 转载:0篇
    • 译文:12篇
    • 评论:1271条
    博客专栏
    最新评论