`
izuoyan
  • 浏览: 8932996 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

设计模式经典32式(Patterns in Java)(前言)

阅读更多
转自板桥里人 http://www.jdon.com 2002/05/24

著名的EJB领域顶尖的专家Richard Monson-Haefel在其个人网站:www.EJBNow.com中极力推荐的GoF的《设计模式》,原文如下:

Design Patterns
Most developers claim to experience an epiphany reading this book. If you've never read the Design Patterns book then you have suffered a very serious gap in your programming education that should be remedied immediately.

翻译: 很多程序员在读完这本书,宣布自己相当于经历了一次"主显节"(纪念那稣降生和受洗的双重节日),如果你从来没有读过这本书,你会在你的程序教育生涯里存在一个严重裂沟,所以你应该立即挽救弥补!

可以这么说:GoF设计模式是程序员真正掌握面向对象核心思想的必修课。虽然你可能已经通过了SUN的很多令人炫目的技术认证,但是如果你没有学习掌握GoF设计模式,只能说明你还是一个技工。

在浏览《Thingking in Java》(第一版)时,你是不是觉得好象这还是一本Java基础语言书籍?但又不纯粹是,因为这本书的作者将面向对象的思想巧妙的融合在Java的具体技术上,潜移默化的让你感觉到了一种新的语言和新的思想方式的诞生。

但是读完这本书,你对书中这些蕴含的思想也许需要一种更明晰更系统更透彻的了解和掌握,那么你就需要研读GoF的《设计模式》了。

《Thingking in Java》(第一版中文)是这样描述设计模式的:他在由Gamma, Helm和Johnson Vlissides简称Gang of Four(四人帮),缩写GoF编著的《Design Patterns》一书中被定义成一个“里程碑”。事实上,那本书现在已成为几乎所有OOP(面向对象程序设计)程序员都必备的参考书。(在国外是如 此)。

GoF的《设计模式》是所有面向对象语言(C++ Java C#)的基础,只不过不同的语言将之实现得更方便地使用。

GOF的设计模式是一座"桥"
就Java语言体系来说,GOF的设计模式是Java基础知识和J2EE框架知识之间一座隐性的"桥"。

会Java 的人越来越多,但是一直徘徊在语言层次的程序员不在少数,真正掌握Java中接口或抽象类的应用不是很多,大家经常以那些技术只适合大型项目为由,避开或 忽略它们,实际中,Java的接口或抽象类是真正体现Java思想的核心所在,这些你都将在GoF的设计模式里领略到它们变幻无穷的魔力。

GoF的设计模式表面上好象也是一种具体的"技术",而且新的设计模式不断在出现,设计模式自有其自己的发展轨道,而这些好象和J2EE .Net等技术也无关!

实 际上,GoF的设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧,让你能够真正掌握接口或抽象 类的应用,从而在原来的Java语言基础上跃进一步,更重要的是,GoF的设计模式反复向你强调一个宗旨:要让你的程序尽可能的可重用。

这其实在向一个极限挑战:软件需求变幻无穷,计划没有变化快,但是我们还是要寻找出不变的东西,并将它和变化的东西分离开来,这需要非常的智慧和经验。

而GoF的设计模式是在这方面开始探索的一块里程碑。

J2EE 等属于一种框架软件,什么是框架软件?它不同于我们以前接触的Java API等,那些属于Toolkist(工具箱),它不再被动的被使用,被调用,而是深刻的介入到一个领域中去,J2EE等框架软件设计的目的是将一个领域 中不变的东西先定义好,比如整体结构和一些主要职责(如数据库操作 事务跟踪 安全等),剩余的就是变化的东西,针对这个领域中具体应用产生的具体不同的变化需求,而这些变化东西就是J2EE程序员所要做的。

由此可见,设计模式和J2EE在思想和动机上是一脉相承,只不过

1.设计模式更抽象,J2EE是具体的产品代码,我们可以接触到,而设计模式在对每个应用时才会产生具体代码。

2. 设计模式是比J2EE等框架软件更小的体系结构,J2EE中许多具体程序都是应用设计模式来完成的,当你深入到J2EE的内部代码研究时,这点尤其明显, 因此,如果你不具备设计模式的基础知识(GoF的设计模式),你很难快速的理解J2EE。不能理解J2EE,如何能灵活应用?

3.J2EE只是适合企业计算应用的框架软件,但是GoF的设计模式几乎可以用于任何应用!因此GoF的设计模式应该是J2EE的重要理论基础之一。

所以说,GoF的设计模式是Java基础知识和J2EE框架知识之间一座隐性的"桥"。为什么说隐性的?

GOF的设计模式是一座隐性的"桥"
因为很多人没有注意到这点,学完Java基础语言就直接去学J2EE,有的甚至鸭子赶架,直接使用起Weblogic等具体J2EE软件,一段时间下来, 发现不过如此,挺简单好用,但是你真正理解J2EE了吗?你在具体案例中的应用是否也是在延伸J2EE的思想?

如 果你不能很好的延伸J2EE的思想,那你岂非是大炮轰蚊子,认识到J2EE不是适合所有场合的人至少是明智的,但我们更需要将J2EE用对地方,那么只有 理解J2EE此类框架软件的精髓,那么你才能真正灵活应用Java解决你的问题,甚至构架出你自己企业的框架来。(我们不能总是使用别人设定好的框架,为 什么不能有我们自己的框架?)

因此,首先你必须掌握GoF的设计模式。虽然它是隐性,但不是可以越过的。

关于本站“设计模式”

Java提供了丰富的API,同时又有强大的数据库系统作底层支持,那么我们的编程似乎变成了类似积木的简单"拼凑"和调用,甚至有人提倡"蓝领程序员",这些都是对现代编程技术的不了解所至.

在真正可复用的面向对象编程中,GoF的《设计模式》为我们提供了一套可复用的面向对象技术,再配合Refactoring(重构方法),所以很少存在简 单重复的工作,加上Java代码的精炼性和面向对象纯洁性(设计模式是java的灵魂),编程工作将变成一个让你时刻体验创造快感的激动人心的过程.

为能和大家能共同探讨"设计模式",我将自己在学习中的心得写下来,只是想帮助更多人更容易理解GoF的《设计模式》。由于原著都是以C++为例, 以Java为例的设计模式基本又都以图形应用为例,而我们更关心Java在中间件等服务器方面的应用,因此,本站所有实例都是非图形应用,并且顺带剖析Jive论坛系统.同时为降低理解难度,尽量避免使用UML图.

如果你有一定的面向对象编程经验,你会发现其中某些设计模式你已经无意识的使用过了;如果你是一个新手,那么从开始就培养自己良好的编程习惯(让你的的程序使用通用的模式,便于他人理解;让你自己减少重复性的编程工作),这无疑是成为一个优秀程序员的必备条件.

整个设计模式贯穿一个原理:面对接口编程,而不是面对实现.目标原则是:降低耦合,增强灵活性.

CSDN的透明特别推崇《建筑的永恒之道》,认为从中探寻到软件的永恒之道,并就"设计模式"写了专门文章《探寻软件的永恒之道 》,其中很多观点我看了很受启发,以前我也将"设计模式" 看成一个简单的解决方案,没有从一种高度来看待"设计模式"在软件中地位,下面是我自己的一些想法:

建筑和软件某些地方是可以来比喻的

特 别是中国传统建筑,那是很讲模式的,这些都是传统文化使然,比如京剧 一招一式都有套路;中国画,也有套路,树应该怎么画法?有几种画法?艺术大家通常是创造出自己的套路,比如明末清初,水墨画法开始成熟,这时画树就不用勾 勒这个模式了,而是一笔下去,浓淡几个叶子,待毛笔的水墨要干枯时,画一下树干,这样,一个活生写意的树就画出来.

我上面这些描述其实都是一种模式,创建模式的人是大师,但是拘泥于模式的人永远是工匠.

再回到传统建筑中,中国的传统建筑是过分注重模式了,所以建筑风格发展不大,基本分南北两派,大家有个感觉,旅游时,到南方,你发现古代名居建筑都差不多;北方由于受满人等少数民族的影响,在建筑色彩上有些与南方迥异,但是很多细节地方都差不多.这些都是模式的体现.

由于建筑受材料和功用以及费用的影响,所用模式种类不多,这点是和软件很大的不同.

正因为这点不同,导致建筑的管理模式和软件的管理模式就有很多不同, 有些人认识不到这点,就产生了可以大量使用"软件蓝领"的想法,因为他羡慕建筑中"民工"的低成本.

要 知道软件还有一个与建筑截然相反的责任和用途,那就是:现代社会中,计划感不上变化,竞争激烈,所有一切变幻莫测,要应付所有这些变化,首推信息技术中的 软件,只有软件能够帮助人类去应付各种变化.而这点正好与建筑想反,建筑是不能帮助人类去应付变化的,(它自己反而要求稳固,老老实实帮助人遮风避雨,总 不能叫人类在露天或树叶下打开电脑编软件吧).

软件要帮助人类去应付变化,这是软件的首要责任, 所以,软件中模式产生的目的就和建筑不一样了,建筑中的模式产生可以因为很多原因:建筑大师的创意;材料的革新等;建筑中这些模式一旦产生,容易发生另外 一个缺点,就是有时会阻碍建筑本身的发展,因为很多人会不思创造,反复使用老的模式进行设计,阻碍建筑的发展.

但 是在软件中,这点正好相反,软件模式的产生是因为变化的东西太多,为减轻人类的负担,将一些不变的东西先用模式固化,这样让人类可以更加集中精力对付变化 的东西,所以在软件中大量反复使用模式(我个人认为这样的软件就叫框架软件了,比如J2EE),不但没阻碍软件的发展,反而是推动了软件的发展.因为其他 使用这套软件的人就可以将更多精力集中在对付那些无法用模式的应用上来.

可以关于建筑和软件中的模式作用可以总结如下:

在软件中,模式是帮助人类向"变化"战斗,但是在软件中还需要和'变化'直接面对面战斗的武器:人的思维,特别是创造 分析思维等等,这些是软件真正的灵魂,这种思维可以说只要有实践需求(如有新项目)就要求发生,发生频度高,人类的创造或分析思维决定了软件的质量和特点。

而在建筑中,模式可以构成建筑全部知识,当有新的需求(如有新项目),一般使用旧的模式都可以完成,因此对人类的创造以及分析思维不是每个项目都必须的,也不是非常重要的,对创造性的思维的需求只是属于锦上添花(除非人类以后离开地球居住了〕。
面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断。如果你还在大量使用if else,当然,界面表现层除外,即使你使用Java/C#这样完全面向对象的语言,也只能说明你的思维停留在传统的面向过程语言上。

传统思维习惯分析

   为什么会业务逻辑层使用if else,其实使用者的目的也是为了重用,但是这是面向过程编程的重用,程序员只看到代码重用,因为他看到if else几种情况下大部分代码都是重复的,只有个别不同,因此使用if else可以避免重复代码,并且认为这是模板Template模式。

  他范的错误是:程序员只从代码运行顺序这个方向来看待它的代码,这种思维类似水管或串行电路,水沿着水管流动(代码运行次序),当遇到几个分管(子管),就分到这几个分管子在流动,这里就相当于碰到代码的if else处了。

   而使用OO,则首先打破这个代码由上向下顺序等同于运行时的先后循序这个规律,代码结构不由执行循序决定,由什么决定呢?由OO设计;设计模式会取代这 些if else,但是最后总是由一个Service等总类按照运行顺序组装这些OO模块,只有一处,这处可包含事务,一般就是Service,EJB中是 Session bean。

  一旦需求变化,我们更多的可能是Service中各个OO模块,甚至是只改动Service中的OO模块执行顺序就能符合需求。

  这里我们也看到OO分离的思路,将以前过程语言的一个Main函数彻底分解,将运行顺序与代码其他逻辑分离开来,而不是象面向过程那样混乱在一起。所以有人感慨,OO也是要顺序的,这是肯定的,关键是运行顺序要单独分离出来。

  是否有if else可以看出你有没有将运行顺序分离到家。

设计模式的切入口

  经常有人反映,设计模式是不错,但是我很难用到,其实如果你使用if else来写代码时(除显示控制以外),就是在写业务逻辑,只不过使用简单的判断语句来作为现实情况的替代者。

   还是以大家熟悉的论坛帖子为例子,如ForumMessage是一个模型,但是实际中帖子分两种性质:主题贴(第一个根贴)和回帖(回以前帖子的帖子),这里有一个朴素的解决方案:
建立一个ForumMessage,然后在ForumMessage加入isTopic这样判断语句,注意,你这里一个简单属性的判断引入,可能导致你的程序其他地方到处存在if else 的判断。

   如果我们改用另外一种分析实现思路,以对象化概念看待,实际中有主题贴和回帖,就是两种对象,但是这两种对象大部分是一致的,因此,我将 ForumMessage设为表达主题贴;然后创建一个继承ForumMessage的子类ForumMessageReply作为回帖,这样,我在程序 地方,如Service中,我已经确定这个Model是回帖了,我就直接下溯为ForumMessageReply即可,这个有点类似向 Collection放入对象和取出时的强制类型转换。通过这个手段我消灭了以后程序中if else的判断语句出现可能。

  从这里体现了,如果分析方向错误,也会导致误用模式。

  讨论设计模式举例,不能没有业务上下文场景的案例,否则无法决定是否该用模式,下面举两个对比的例子:

  第一. 这个帖子中举例的第一个代码案例是没有上下文的,文中只说明有一段代码:

main() {

if(case A){

//do with strategy A

}else(case B){

//do with strategy B

}else(case C){

//do with strategy C

}

}

  这段代码只是纯粹的代码,没有业务功能,所以,在这种情况下,我们就很难确定使用什么模式,就是一定用策略模式等,也逃不过还是使用if else的命运,设计模式不是魔法,不能将一段毫无意义的代码变得简单了,只能将其体现的业务功能更加容易可拓展了。

  第二.在这个帖子中,作者举了一个PacketParser业务案例,这段代码是体现业务功能的,是一个数据包的分析,作者也比较了各种模式使用的不同,所以我们还是使用动态代理模式或Command模式来消灭那些可能存在的if else

  由以上两个案例表明:业务逻辑是我们使用设计模式的切入点,而在分解业务逻辑时,我们习惯则可能使用if else来实现,当你有这种企图或者已经实现代码了,那么就应该考虑是否需要重构Refactoring了。

if else替代者

  那么实战中,哪些设计模式可以替代if else呢?其实GoF设计模式都可以用来替代if else,我们分别描述如下:


  • 状态模式 
      当数据对象存在各种可能性的状态,而且这种状态将会影响到不同业务结果时,那么我们就应该考虑是否使用状态模式,当然,使用状态模式之前,你必须首先 有内存状态这个概念,而不是数据库概念,因为在传统的面向过程的/面向数据库的系统中,你很难发现状态的,从数据库中读取某个值,然后根据这个值进行代码 运行分流,这是很多初学者常干的事情。参考文章:状态对象:数据库的替代者
      使用传统语言思维的情况还有:使用一个类整数变量标识状态:

    public class Order{

    private int status;

    //说明:

    //status=1 表示订货但为查看 ;

    //status=2 表示已经查看未处理;

    //status=3 表示已经处理未付款

    //status=4 表示已经付款未发货

    //status=5 表示已经发货

    }


      上述类设计,无疑是将类作为传统语言的函数来使用,这样导致程序代码中存在大量的if else。

  • 策略模式 
      当你面临几种算法或者公式选择时,可以考虑策略模式,传统过程语言情况是:从数据库中读取算法数值,数值1表示策略1,例如保存到数据库;数值为2表 示策略2,例如保存到XMl文件中。这里使用if else作为策略选择的开关。

  • command模式 
      传统过程的思维情况是:如果客户端发出代号是1或"A",那么我调用A.java这个对象来处理;如果代号是2或"B",我就调用B.java来处 理,通过if else来判断客户端发送过来的代码,然后按事先约定的对应表,调用相应的类来处理。

  • MVC模式 
      MVC模式的传统语言误用和Command模式类似,在一个Action类中,使用if else进行前后台调度,如果客户端传送什么命令;我就调用后台什么结果;如果后台处理什么结构,再决定推什么页面,不过,现在我们使用 Struts/JSF这样MVC模式的框架实现者就不必范这种低级错误。

  • 职责链模式 
      职责链模式和Command模式是可选的,如果你实在不知道客户端会发出什么代号;也没有一个事先定义好的对照表,那么你只能编写一个个类去碰运气一样打开这个包看一下就可以。与Command是不同在AOP vs Decorator一文中有分析。

  • 代理或动态代理模式 
      代理对象可以是符合某种条件的代表者,比如,权限检验,传统面向过程思维是:当一个用户登陆后,访问某资源时,使用if else进行判断,只有某种条件符合时,才能允许访问,这样权限判断和业务数据逻辑混乱在一起,使用代理模式可以清晰分离,如果嫌不太好,使用动态代理, 或者下面AOP等方式。

  • AOP或Decorator模式
      
      其实使用filter过滤器也可以替代我们业务中的if else,过滤器起到一种过滤和筛选作用,将符合本过滤器条件的对象拦截下来做某件事情,这就是一个过滤器的功能,多个过滤器组合在一起实际就是if else的组合。
      所以,如果你实在想不出什么办法,可以使用过滤器,将过滤器看成防火墙就比较好理解,当客户端有一个请求时,经过不同性质的防火墙,这个防火墙是拦截 端口的;那个防火墙是安全检查拦截等等。过滤器也如同红蓝白各种光滤镜;红色滤镜只能将通过光线中的红色拦截了;蓝色滤镜将光线中的蓝色拦截下来,这实际 上是对光线使用if else进行分解。


      如图,通过一个个条件过滤器我们立体地实现了对信号的分离,如果你使用if else,说明你是将图中的条件1/2/3/4合并在一起,在同一个地方实现条件判断。
      需要深入了解过滤器的实现细节和微小区别,请参考文章:AOP vs Decorator
  • OO设计的总结  

      还有一种伪模式,虽然使用了状态等模式,但是在模式内部实质还是使用if else或switch进行状态切换或重要条件判断,那么无疑说明还需要进一步努力。更重要的是,不能以模式自居,而且出书示人。

       真正掌握面向对象这些思想是一件困难的事情,目前有各种属于揪着自己头发向上拔的解说,都是误人子弟的,所以我觉得初学者读Thinking in Java(Java编程思想)是没有用,它试图从语言层次来讲OO编程思想,非常失败,作为语言参考书可以,但是作为Java体现的OO思想的学习资料, 就错了。

      OO编程思想是一种方法论,方法论如果没有应用比较,是无法体会这个方法论的特点的,禅是古代一个方法论,悟禅是靠挑水砍柴这些应用才能体会。

       那么OO思想靠什么应用能够体会到了?是GoF设计模式,GoF设计模式是等于软件人员的挑水砍柴等基本活,所以,如果一个程序员连基本活都不会,他何 以自居OO程序员?从事OO专业设计编程这个工作,如果不掌握设计模式基本功,就象一个做和尚的人不愿意挑水砍柴,他何以立足这个行业?早就被师傅赶下 山。

      最后总结:将if else用在小地方还可以,如简单的数值判断;但是如果按照你的传统习惯思维,在实现业务功能时也使用if else,那么说明你的思维可能需要重塑,你的编程经验越丰富,传统过程思维模式就容易根深蒂固,想靠自己改变很困难;建议接受专业头脑风暴培训

      用一句话总结:如果你做了不少系统,很久没有使用if else了,那么说明你可能真正进入OO设计的境地了。(这是本人自己发明的实战性的衡量考核标准)。

    分享到:
    评论

    相关推荐

    Global site tag (gtag.js) - Google Analytics