设计模式之我见陈玉好联系电话:1523163855邮箱:1307041983@刚开始学习设计模式的时候,感到这些模式真的非常抽象。
今年下半年以来,随着我们组工作重点的转移,以及我在小组中角色的变化,我开始有条件提出自己对新系统的设计想法。
在设计过程中,我发现了很多设计模式的用处,也确实应用了很多设计模式,这让我越来越感到设计模式的重要性,因此我写了这十余篇专门介绍设计模式的文章,作为我的学习笔记。
《设计模式——可复用的面向对象软件的基础》(有趣的是,梅宏一再在组会上强调应该译成重用)中介绍了一共23种设计模式,我一共写了19个设计模式(其中三个和在一篇文章中),余下四个,考虑到该模式的应用范围我就没有介绍。
在写这些文章时,其中的很多例子都是我在实践中提炼出来的,当然也有很大一部分是《设计模式》中的例子。
不过,这四个人(四人团)生活的年代里现在已经很远了,所以它们的例子也很古老。
让我们更加设计模式设计模式是个好东西,它给出了很多设计中的技巧与思路,对于很多优秀的设计,它加以总结与提炼。
设计模式并非四人团拍脑瓜想出来的,而是他们搜集了其他人优秀的设计,加以整理出来的,他们不是这些模式的创造者,仅仅是整理者。
应用设计模式会给我们带来很多好处:软件将变得更加灵活,模块之间的耦合度将会降低,效率会提升,开销会减少。
更重要的,设计模式就好像美声唱法中的花腔,让你的设计更加漂亮。
总的来说,设计模式似乎将软件设计提升到艺术的层次。
设计模式已经被广泛的应用了,在现在很多的图形界面框架都使用了MVC模式,大量跌代器模式的应用,彻底改变了我们对集合的操作方式。
不仅如此,应用了设计模式的设计,往往被看成为优秀的设计。
这是因为,这些设计模式都是久经考验的。
在学习和使用设计模式的时候,往往出现一个非常严重的误区,那就是设计模式必须严格地遵守,不能修改。
但是设计模式不是设计模型,并非一成不变。
正相反,设计模式中最核心的要素并非设计的结构,而是设计的思想。
只有掌握住设计模式的核心思想,才能正确、灵活的应用设计模式,否则再怎么使用设计模式,也不过是生搬硬套。
当然,掌握设计模式的思想,关键是要仔细研究模式的意图和结构。
一个模式的意图,就是使用这个设计模式的目的,体现了为什么要使用这个模式,也就是需求问题。
这个模式的结构,就是如何去解决这个问题,是一种手段、一种经典的解决方法,这种解决方法只是一种建议。
两个方面结合起来,明白为什么需要设计模式,同时明白了如何实现这个模式,就容易抓住模式的本质思想。
在抓住意图和结构的基础上,实践也是掌握设计模式的必要方法。
当然,设计模式必须在某个场景下得到应用才有意义,这也是为什么《设计模式》中提供了大量的例子用来说明模式的应用场景,这实际上为读者提供了一种上下文环境。
学外语不是要强调“语言环境”么,学习设计模式也是这样。
不要设计模式看到网上很多人在讨论设计模式,他们确实很有***,满嘴都是模式的名字,恨不得写个Hello World都要应用到设计模式。
设计模式确实是好东西,但是,中国有句古话叫作物极必反,即便是按照辩证法,事物总要一分为二的看。
我们说设计模式的目的是为了让软件更加灵活,重用度更高。
但是,某种意义上,设计模式增加了软件维护的难度,特别是它增加了对象之间关联的复杂度。
我们总说,重用可以提高软件开发的效率。
如果你是大牛,你自然希望你的设计可以被反复使用10000年,那就是:当世界毁灭的时候,你的设计依然存在。
然而,现实是一个系统的设计往往在5年之内就会被抛弃,这是因为:1,软件技术产生了新的变化,使用新的技术进行的设计,无论如何都比你的设计好;2,硬件环境发生了很大变化,你的设计里对开销或者效率的追求已经没有意义了;3,新的大牛出现了,并且取代了你的位置。
应用设计模式会导致设计周期的加长(因为更复杂了),但是很多项目还在设计阶段就已经胎死腹中,再好的设计也没有发挥的余地。
当我们向设计模式顶礼膜拜的时候,我们还必须清醒地看到软件生产中非技术层面上的东西往往具有决定性作用。
理想固然崇高,但现实总是残酷的。
如何看清理想与现实的界限,恐怕是需要我们在实践中不断磨砺而体会出来的。
在看完设计模式后,不妨反问以下自己,这些模式究竟能给你带来什么?Interpreter、Iterator、State模式(解释器模式、迭代器模式、状态模式)Interpreter模式:这个模式主要试图去解释一种语言。
如果你学过形式语言,那么这个模式对你来说是多余的。
Iterator模式:这个模式试图隐藏集合的内部表示,又同时可以使用户依次访问集合中的元素。
现在STL和Java的跌代器就是应用这个模式的结果。
State模式:这个模式的意图是允许对象在其状态改变时修改其行为,好像对象改变了。
这个模式的应用场景是当对象的行为依赖于对象的状态时。
为了实现这个模式,我们可以为每个状态下的行为实现一个类,当对象的状态发生改变,它调用不同状态对象的实例方法。
注意,以前可能需要使用switch或者if语句进行分支转换,现在则利用多态机制完成。
Flyweight模式(享元模式)这个模式利用共享有效的支持大量的细粒度的对象。
比如,编辑软件中,一篇文章有很多个字符,我们可以对每个字符对象生成一个对象,如果这篇文章有几M个文字,那么对象的数量肯定是不能容忍的。
使用Flyweight模式,我们将所有的文字对象共享起来,文章中的字符仅仅是指向共享池中的某个对象的索引。
在这里要搞清楚一件事情,利用Flyweight模式不会有效地减少信息的数量(也就是软件的空间开销),因为无论是否共享,表达这么多信息所需要的编码数量是一定的,所以开销不会大幅减小。
只是,这个模式会减少系统中对象的数量,因为大量的对象会被共享。
在编辑软件中,字符对象被共享,那么一篇文章中的文字,可以按照段落、格式等等进行结组,一组文字构成一个对象,这样对象从单个文字变成一组文字,数量大幅减少。
在使用Flyweight模式需要注意的一点,由于对象被共享了,因此这些对象没有各自的属性,那么根据上下文环境,我们在使用这些对象的时候,必须向它传递一些参数。
在编辑软件中,这些参数可能就是字体、字号、颜色等等信息。
使用Flyweight模式还有一个好处,那就是我们可以在不修改系统的情况下增加享元。
Command模式(命令模式)Command模式,将一个请求封装为一个对象。
这样,你可以向客户端发送不同请求的参数,排队或记录请求,同时可以支持不能执行的请求。
在软件中,不同的模块、对象之间经常会各种调用,或者我们称之为请求。
传统的方法,我们将请求实现为函数调用。
这样做是最简单的方法,但却在无形之中增加了模块之间的耦合度。
当请求发生很大变化的时候,系统将变得很难维护。
与此同时,当服务端(接受请求的一端)增加或者删除一个请求的时候,按照传统的方法,客户端(发送请求的一端)也必须重新编译(这一点在删除请求的时候最明显),这样系统才能正确运行。
使用Command模式的一个核心思想就是,服务端提供一个统一的请求处理接口,客户端则通过调用接口向服务端发送请求,这些请求被封装成对象的形式(或者其等价形式)。
在《设计模式》中,“四人团”并没有强调统一接口的事情,它强调了另一个方面,那就是封装请求。
事实上,封装一个请求总是要求有一个地方来接受和处理这个请求的,这个地方实际上就是统一请求接口。
在《设计模式》中,请求被封装成一个Command对象,这个对象保存着请求类型、参数等信息,服务端收到这个命令后就会执行Command对象中的Execute()函数,这个函数具体实现了真正的操作。
这种实现方法可以保证增加新的请求而不必重新编译服务端。
我个人认为,Command模式的另一个形式就是在服务端实现各种操作,Command 对象只是负责指明请求的类型,这样,当服务器端发现请求不正确时,可以忽略该请求。
和上一种形式相比,这种形式更加简洁(因为可以不真正实现Command对象,在C++中可以使用不定参数实现),但是缺少灵活性。
Command模式使得记录请求成为了可能,我们可以捕获系统中的请求对象,记录他们。
Composite模式(组合模式)Composite模式的意图是“将对象组合成树形结构表示…整体-部分‟的层次结构。
Composite使得用户对单个对象和组合对象的使用更具有一致性”。
在Word中我们经常会将一些图元进行“组合”,组合以后的图形还可以向简单图元那样进行移动、变形等等操作;除此以外,在Word中,我们对于一个字符、一个词组、一句话、一个段落,甚至是整篇文章的操作是相同的,我们都可以进行剪切、复制,进行字体与大小的调整,进行颜色的变换。
这些例子都是Composite模式的实例,我们将简单的元素组合成复杂的元素,然后还可以像操作简单元素那样操作组合元素。
Composite模式将子元素组织成树型,实际上,组织成图型也没有问题。
用户总是喜欢组合简单元素,一方面,用户可以通过这样的组合来进行抽象,另一方面,用户可以通过组合化简繁琐的操作。
Composite模式在各种可视化编辑软件中应用得最为广泛。
另一使用Composite的经典例子是Java的Swing系统。
所有的Swing组件都是继承自一个叫做JComponent的接口,因此,我们对一个JFrame的操作和对一个JButton 的操作是一样的。
这同时也使得,JFrame在管理自己的子元素时,它不需要知道他们是一个JButton还是一个JPanel,对它来说,这只是一个JComponent。
实现Composite模式的关键是良好设计的接口,人们应该对可能的元素(简单的、组合的)进行分析,并设计出通用的操作。
尽可能的保证接口操作对所有元素都是有意义的,否则就应该将那些只对部分元素有意义的操作下放到子类中。
Proxy模式(代理模式)按照“四人团”的说法,Proxy模式可以为控制另一个对象而提供一个代理或者占位符。
这个模式可以使我们在真正需要的时候创建对象,如果我们不需要这个对象,Proxy模式会为我们提供一个占位符。
如果我们有大量这样消耗很大的对象的时候,我们就可以使用Proxy模式,初始情况下,Proxy模式只会提供占位符而不会真正创建对象,但是对于使用者来说,他看到是真正的对象而不是一个代理。
一旦使用者需要获得或者更改对象属性的时候,Proxy模式就会创建该对象,在此之后,我们就可以通过代理访问真正的对象了。
在Word里面应该是使用了Proxy模式。
打开一篇含图的很长的文档时,大部分的图片都不会被载入,而仅仅是提供占位符,只有当用户准备察看这一页的时候,代理才会真正载入图片。
和Singleton模式一样,Proxy模式都是保证我们可以按需分配对象,不同的是,Singleton模式还会保证在全局范围内使用同一个对象实例,而Proxy则没有这个功能。