讨论:关于设计模式

ajex 2003-08-01 04:37:47
最近看一些网站关于设计模式的文章,什么mvc,observer,哪位仁兄对设计模式有研究.讲讲!
下周要出差,郁闷~~
...全文
45 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
hanfey 2004-01-02
  • 打赏
  • 举报
回复
marking && learning
zpisgod 2003-08-21
  • 打赏
  • 举报
回复
学习
plife 2003-08-21
  • 打赏
  • 举报
回复
学习
ajex 2003-08-21
  • 打赏
  • 举报
回复
精彩!
呵呵,iamsea11看来对设计模式很有研究.
lmdhit 2003-08-20
  • 打赏
  • 举报
回复
mvc :modele,view,control
设计模式我也在学习中
林仪明 2003-08-20
  • 打赏
  • 举报
回复
楼上的这篇好!

设计模式是为了解决问题的,问题的定义才是应用设计模式的关键!
iamsea11 2003-08-20
  • 打赏
  • 举报
回复
可以看出,对于新的错误类型的增加,仅仅需要增加一个具体的错误类,对于错误处理部分没有任何影响。看上去很完美,也符合OCP原则,但是进一步分析就会发现,这个方案一样存在着问题,我们将在下一个小节进行详细的说明。



5、进一步分析



上一个小节给出了一个方案,对于只有GUISys这一个错误处理者是很完美的,但是情况往往不是这样的。前面也曾经提到过,对于发生的错误,除了要通知系统的使用者外,还要进行其他的处理,比如:试图恢复,记如日志等。可以看出,这些处理方法和把错误通告给使用者是非常不同的,完全没有办法仅仅用一个handle方法来统一所有的不同的处理。但是,如果我们在ErrorBase中增加不同的处理方法声明,在具体的错误类中,根据自身的需要来相应的实现这些方法,好像也是一个不错的方案。代码示例如下:



interface ErrorBase

{

public void guiHandle();

public void logHandle();

}



class DBAccessError implements ErrorBase

{

public void guiHandle() {

/* 通知用户界面的数据库访问错误处理 */

}



public void logHandle() {

/* 通知日志系统的数据库访问错误处理 */

}

}



class CommunicationError implements ErrorBase

{

public void guiHandle() {

/* 通知用户界面的通信错误处理 */

}



public void logHandle() {

/* 通知日志系统的通信错误处理 */

}

}



class GUISys

{

public void announceError(ErrorBase error) {

error.guiHandle();

}

}



class LogSys

{

public void announceError(ErrorBase error) {

error.logHandle();

}

}





读者可能已经注意到,这种做法其实也不是十分符合OCP,虽然它把变化局限在ErrorBase这个类层次架构中,但是增加新的处理方法,还是更改了已经存在的ErrorBase类。其实,这种设计方法,还违反了另外一个著名的面向对象的设计原则:SRP(Single Responsibility Principle)。这个原则的核心含意是:一个类应该有且仅有一个职责。关于职责的含意,面向对象大师Robert.C Martin有一个著名的定义:所谓一个类的职责是指引起该类变化的原因,如果一个类具有一个以上的职责,那么就会有多个不同的原因引起该类变化,其实就是耦合了多个互不相关的职责,就会降低这个类的内聚性。错误类的职责就是,保存和自己相关的错误状态,并且提供方法用于获取这些状态。上面的设计中把不同的处理方法也放到错误类中,从而增加了错误类的职责,这样即使和错误类本身没有关系的对于错误处理方式的变化,增加、修改都会导致错误类的修改。这种设计方法一样会在需求变化时,带来没有预料到的问题。那么能否将对错误的处理方法从中剥离出来呢?如果读者比较熟悉设计模式(这里的熟悉是指,设计模式的意图、动机,而不是指怎样去实现一个具体的设计模式),应该会隐隐约约感觉到一个更好的设计方案即将出现。



6、设计模式浮出水面



让我们对问题重新描述一下:我们已经有了一个关于错误的类层次结构,现在我们需要在不改变这个类层次结构的前提下允许我们增加对于这个类层次的新的处理方法。听起来很耳熟吧,不错,这正是过于visitor设计模式的意图的描述。通过对于该模式动机的分析,我们很容易知道,要想使用visitor模式,需要定义两个类层次:一个对应于接收操作的元素的类层次(就是我们的错误类),另一个对应于定义对元素的操作的访问者(就是我们的对于错误的不同处理方法)。这样,我们就转换了问题视角,即把需要不同的错误处理方法的问题转变为需要不同的错误处理类,这样的结果就是我们可以通过增加新的模块(类)来增加新的错误处理方法,而不是通过增加新的错误处理方法(这样做,就势必要修改已经存在的类)。

iamsea11 2003-08-20
  • 打赏
  • 举报
回复
设计模式是对特定问题经过无数次经验总结后提出的能够解决它的优雅的方案。但是,如果想要真正使设计模式发挥最大作用,仅仅知道设计模式是什么,以及它是如何实现的是很不够的,因为那样就不能使你对于设计模式有真正的理解,也就不能够在自己的设计中正确、恰当的使用设计模式。本文试图从另一个角度(设计模式的意图、动机)来看待设计模式,通过这种新的思路,设计模式会变得非常贴近你的设计过程,并且能够指导、简化你的设计,最终将会导出一个优秀的解决方案。



1、介绍



在进行项目的开发活动中,有一些设计在项目刚刚开始工作的很好,但是随着项目的进展,发现需要对已有的代码进行修改或者扩展,导致这样做的原因主要有:新的功能需求的需要以及对系统进一步理解。在这个时候,我们往往会发现进行这项工作比较困难,即使能完成也要付出很大的代价。此时,一个必须要做的工作就是要对现有的代码进行重构(refactoring),通过重构使得我们接下来的工作变得相对容易。



重构就是在不改变软件系统代码的外部行为的前提下,改善它的内部结构。重构的目标就是使代码结构更加合理,富有弹性,能够适应新的需求、新的变化。对于特定问题给出优美解决方案的设计模式往往会成为重构的目标,而且一旦我们能够识别出能够解决我们问题的设计模式,将会大大简化我们的工作,因为我们可以重用别人已经做过的工作。但是在我们的原始设计和最终可能会适用于我们的设计模式间的过渡并不是平滑的,而是有一个间隙。这样的结果就是:即使我们已经知道了很多的设计模式,面对我们的实际问题,我们也没有一个有效的方法去判断哪一个设计模式适用于我们的系统,我们应该去怎样应用它。



造成上述问题的原因往往是由于过于注重设计模式所给出的解决方案这个结果,而对于设计模式的意图,以及它产生的动机却忽略了。然而,正是设计模式的意图、动机促使人们给出了一个解决一类问题的方案这个结果,设计模式的动机、意图体现了该模式的形成思路,所以更加贴近我们的实际问题,从而会有效的指导我们的重构历程。本文将通过一个实例来展示这个过程。



在本文中对例子进行了简化,这样做是为了突出问题的实质并且会使我们的思路更加清晰。思路本身才是最重要、最根本的,简化了的例子不会降低我们所展示的思路、方法的适用性。



2、问题描述



一个完善的软件系统,必须要对出现的错误进行相应的处理,只有这样才能使系统足够的健壮,我准备以软件系统中对于错误的处理为例,来展示我所使用的思路、方法。



在一个分布式的网管系统中,一个操作往往不会一定成功,常常会因为这样或者那样的原因失败,此时我们就要根据失败的原因相应的处理,使错误的影响局限在最小的范围内,最好能够恢复而不影响系统的正常运行,还有一点很重要,那就是在对错误进行处理的同时,一定不要忘记通知系统的管理者,因为只有管理者才有能力对错误进行进一步的分析,从而查找出错误的根源,从根本上解决错误。



下面我就从错误处理的通告管理者部分入手,开始我们的旅程。假定一个在一个分布式环境中访问数据库的操作,那么就有可能因为通信的原因或者数据库本身的原因失败,此时我们要通过用户界面来通知管理者发生的错误。简化了的代码示例如下:





/* 错误码定义 */

class ErrorConstant

{

public static final int ERROR_DBACCESS = 100;

public static final int ERROR_COMMUNICATION = 101;

}



/* 省略了用户界面中的其他的功能 */

class GUISys

{

public void announceError(int errCode) {



switch(errCode) {



case ErrorConstant.ERROR_DBACCESS:

/* 通告管理者数据库访问错误的发生*/

break;



case ErrorConstant.ERROR_COMMUNICATION:

/* 通告管理者通信错误的发生*/

break;



}

}

}





开始,这段代码工作的很好,能够完成我们需要的功能。但是这段代码缺少相应的弹性,很难适应需求的变化。



3、问题分析



熟悉面向对象的读者很快就会发现上面的代码是典型的结构化的方法,结构化的方法是以具体的功能为核心来组织程序的结构,它的封装度仅为1级,即仅有对于特定的功能的封装(函数)。这使得结构化的方法很难适应需求的变化,面向对象的方法正是在这一点上优于结构化的方法。在面向对象领域,是以对象来组成程序结构的,一个对象有自己的职责,通过对象间的交互来完成系统的功能,这使得它的封装度至少为2级,即封装了为完成自己职责的方法和数据。另外面向对象的方法还支持更高层次的封装,比如:通过对于不同的具体对象的共同的概念行为进行描述,我们可以达到3级的封装度- 抽象的类(在Java中就是接口)。封装的层次越高,抽象的层次就越高,使得设计、代码有越高的弹性,越容易适应变化。



考虑对上一节中的代码,如果在系统的开发过程中发现需要对一种新的错误进行处理,比如:用户认证错误,我们该如何做使得我们的系统能够增加对于此项功能的需求呢?一种比较简单、直接的做法就是在增加一条用来处理此项错误的case语句。是的,这种方法的确能够工作,但是这样做是要付出代价的。



首先,随着系统的进一步开发,可能会出现更多的错误类型,那么就会导致对于错误的处理部分代码冗长,不利于维护。其次,也是最根本的一点,修改已经能够工作的代码,很容易引入错误,并且在很多的情况下,错误都是在不经意下引入的,对于这种类型的错误很难定位。有调查表明,我们在开发过程中,用于修正错误的时间并不多,大部分的时间是在调试、发现错误。在面向对象领域,有一个很著名的原则:OCP(Open-Closed Principle),它的核心含意是:一个好的设计应该能够容纳新的功能需求的增加,但是增加的方式不是通过修改又有的模块(类),而是通过增加新的模块(类)来完成的。如果一个设计能够遵循OCP,那么就能够有效的避免上述的问题。



要是一个设计能够符合OCP原则,就要求我们在进行设计时不能简单的以功能为核心。要实现OCP的关键是抽象,抽象表征了一个固定的行为,但是对于这个行为可以有很多个不同的具体实现方法。通过抽象,我们就可以用一个固定的抽象的概念来代替哪些容易变化的数量众多的具体的概念,并且使得原来依赖于哪些容易变化的概念的模块,依赖于这个固定的抽象的概念,这样的结果就是:系统新的需求的增加,仅仅会引起具体的概念的增加,而不会影响依赖于具体概念的抽象体的其他模块。在实现的层面上,抽象体是通过抽象类来描述的,在Java中是接口(interface)。关于OCP的更详细描述,请参见参考文献[2]。



既然知道了问题的本质以及相应的解决方法,下面就来改善我们的代码结构。



4、初步方案



让我们重新审视代码,看看该如何进行抽象。在错误处理中,需要处理不同类型的错误,每个具体的错误具有特定于自己本身的一些信息,但是它们在概念层面上又是一致的,比如:都可以通过特定的方法接口获取自已内部的错误信息,每一个错误都有自己的处理方法。由此可以得到一个初步的方案:可以定义一个抽象的错误基类,在这个基类里面定义一些在概念上适用于所有不同的具体错误的方法,每个具体的错误可以有自己的不同的对于这些方法的实现。代码示例如下:



interface ErrorBase

{

public void handle();

public String getInfo();

}



class DBAccessError implements ErrorBase

{

public void handle() {

/* 进行关于数据库访问错误的处理 */

}



public String getInfo() {

/* 构造返回关于数据库访问错误的信息 */

}

}



class CommunicationError implements ErrorBase

{

public void handle() {

/* 进行关于通信错误的处理 */

}



public String getInfo() {

/* 构造返回关于通信错误的信息 */

}

}





这样,我们就可以在错误发生处,构造一个实际的错误对象,并以ErrorBase引用它。然后,交给给错误处理模块,此时错误处理模块就仅仅知道一个类型ErrorBase,而无需知道每一个具体的错误类型,这样就可以使用统一的方式来处理错误了。代码示例如下:



class GUISys

{

public void announceError(ErrorBase error) {

/* 使用一致的方式处理错误 */

error.handle();

}

}





林仪明 2003-08-20
  • 打赏
  • 举报
回复
7.对《Enterprise solution Patterns》的介绍
我更愿意把它看成是微软对Martin Flower 的《企业应用架构模式》的微软版解释。因为在《企业应用架构模式》中的代码是java,而微软为此提供了c#代码。呵呵!
林仪明 2003-08-20
  • 打赏
  • 举报
回复
以下谈一下自己学习设计模式的感受:
谈之前先说一下偶学习的书籍《设计模式》(GoF)《企业应用架构模式》(P of EAA,Martin Flower),《Enterprise Solution Patterns》(Microsoft)。

1.设计模式的目的——可复用
面向对象的应用本来是要解决可复用的问题,不幸的是,可复用只险于界面组件(大家都见多了),但是最核心的商业应用上,几乎还是没有什么变化。
为什么?商业应用的复杂性比界面组件高的多。那么可复用是不是不可能实现了吗?
不是,可复用是可以。
a.面向对象还是在机制上提供一种可复用可能。
b.大量优秀的程序员在开发过程中,积累了大量的开发经验,针对同一类似要求的问题,形成一定的开发方法。虽然不同的问题的解决方法不尽相同,但是从数学角度看是一种同态或者同构。

2.如何实现可复用
1.既然仅仅靠语言无法解决可复用的问题,那么就在语言之上的设计方法上加以改进。
2.既然商业应用是复杂的,那么就尽可能的降低复杂度。
如何降低复杂度————解藕。
如何解藕————加入中间层。
等一等,听起来不错,不过本来就很复杂了,在加一层,几层,不是更复杂。
没有错!中间层不是随便可以加的。
——那我们怎么办?
——答案是经验。我们就到了第三个话题。
3.把经验变成模式。
或者可以用另外一个写法,什么是设计模式
3.1 名称
3.2 问题
3.3 解决方案
3.4 效果
当经验是针对某个问题的,有一整个解决方法,效果比较好(开发效率、运行效率和维护效率),经验就成为一种模式。

4.对《设计模式》的介绍
4.1 创建型模式——允许你在运行时在一条生产线上制造出不同型号的产品。
4.2 结构型模式——允许你利用已有的类组合出更强大的功能
4.3 行为型模式——让你有效的管理对象间的通信。

5.对《企业应用架构模式》的介绍
5.1 逻辑层模式(Domain Layer Pattern)——如何有效组织逻辑层
5.2 web server pattern
5.3 object/ration mapping pattern ——企业中最重要的就是数据,而企业的数据又是庞大而复杂的,相信做过企业开发的朋友对此深有感受。如何高效把数据映射到我们的业务实体中,成为企业开发的一个重点和难点。这里 Martin Flower为我们提供很多的模式
5.4 同步模式(Concurrency Pattern)——在分布式多用户模式下,数据同步成为与数据映射一样重要的问题。martin Flower再次帮了我们一个大忙。
5.5 分布式模式(Distribution Pattern)——不用我多说了吧。
5.6 一些基本的模式——包括Gateway、plugin等,我们会在开发中常常要做的。

6.《设计模式》国内有翻译版和影印版
《企业应用架构模式》国内还没有引进,只有英文版的。自己也是边看边学,做了一些笔记,现在回头看发现只是原作的中文翻译。呵呵!
AllRegistered 2003-08-20
  • 打赏
  • 举报
回复
有一本《c#设计模式〉,谁看过?我正想有空看看,设计模式是个好东东
ufinger 2003-08-20
  • 打赏
  • 举报
回复
很有用的东西,目前的经历里面工厂方法运用得比较多
电子工业出版社那本中国人写的书,叫java与模式的,对于入门非常好,强烈推荐

只要有一点面向对象的基础知识,很容易看懂
ajex 2003-08-20
  • 打赏
  • 举报
回复
明天结账.
myadaidarling 2003-08-05
  • 打赏
  • 举报
回复
以前设计模式看了一点点,也没弄懂,什么简单工厂模式...

看的直迷糊,现在心挺静的想再来一遍,找不到书和时间了。:(
风影2000 2003-08-04
  • 打赏
  • 举报
回复
不明白。努力学习……
ajex 2003-08-04
  • 打赏
  • 举报
回复
楼上的这位兄弟,我对java一窃不通。很遗憾,这本书我也没有。
怎么没有高手出来说两句呢,
今天到上海出差,很不爽,郁闷ing
myadaidarling 2003-08-01
  • 打赏
  • 举报
回复
ajex (仙桃人) 是否有 《java与模式》的书的电子版本?

能否给我寄过来,谢谢!

email : fanchen@yuntai.net
susanyw 2003-08-01
  • 打赏
  • 举报
回复
正在学习中。关注!
goody9807 2003-08-01
  • 打赏
  • 举报
回复
关注~~
niqiu322 2003-08-01
  • 打赏
  • 举报
回复
gz
加载更多回复(4)

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧