如何向妻子解释设计模式

标签: 妻子 解释 设计模式 | 发表时间:2011-05-30 09:55 | 作者:倪大虾 brett80
出处:http://www.cnblogs.com/

简介

  自上篇翻译<如何向妻子解释OOD>后收到了很好的反应。故特继续翻译作者的<How I explained Design Patterns to my wife: Part 1>一文,以飨读者。在此文中,作者依旧通过与妻子浅显易懂的对话,向读者解释了什么是设计模式。

设计模式是什么?

Shubho:通过我们关于面向对象设计原则(OODP,即SOLID原则)的对话,我想你已经对面向对象设计原则(OODP)有了基本的认识。希望你不要介意我把对话分享到博客上。你可以在这找到它:<如何向妻子解释OOD>.

设计模式是这些原则在某些特定公共场景下标准化的应用,接下来让我们通过一些例子学习什么是设计模式。

Farhana: 当然,我喜欢例子。

Shubho: 让我们以汽车为例讨论一下。汽车是一个很复杂的对象,由成千上万的其它对象组成,如发动机,车轮,方向盘,车座,车体等等其他不同的部分或部件。

汽车部件

当装配汽车时,制造商需要集中并装配这些更小的自成汽车子系统的不同部件。而这些不同的小部件同样也是复杂的对象,其它制造商同样要生产并组装它们。在生产汽车时,汽车公司并不会为怎么生产组装这些部件操心(前提是他们要确保这些对象/设备的质量)。当然,汽车制造商更加关心怎么装配这些不同部件以便能生产不同型号的汽车。

通过遵循不同的设计,组装不同的部件,生产不同型号的汽车

Farhana: 汽车制造公司必须有如何生产不同型号汽车的设计图或蓝图,对吗?

Shubho: 当然,并且这些设计都是良好的,他们花费大量的时间和精力来做这些设计。一旦设计完成,生产汽车就仅仅是照葫芦画瓢了。

Farhana: 嗯。如果事先有一些好的设计,就能在短时间内遵照这些设计生产不同产品,并且制造商在每次生产某一个型号产品时就不需要重新设计或重新发明车轮,他们只需要按照已有的设计办事就行了。

生产不同型号产品(汽车)的不同设计图

Shubho: 你抓到重点了。现在假设我们是软件生产商,我们使用基于需求而来的不同组件或功能构建各种不同的软件程序。当生产这些不同软件系统时,我们常常需要为一些不同软件系统中存在的相同情况开发代码,对吗?

Farhana: 是的,在开发不同软件程序时经常遇到相同的设计问题。

Shubho: 我们尝试使用面向对象的方式开发软件,并尝试应用OOPD来让代码能易于维护,可复用,可扩展。无论什么时候,当我们遇到这些设计问题时,如果我们有一组经过谨慎开发,良好测试的对象以供使用会不会更好呢?

Farhana: 是的,这样能够节省时间,生产出更好的软件,且利于以后维护。

Shubho: 很好!从设计上来说,它的好处是你不需要开发那些对象。经过多年发展,人们已经遇到过一些类似的设计问题,并已经形成有一些公认的,良好的已标准化的设计方案。我们称之为设计模式。

我们一定好感谢四人组,他们在《设计模式:可复用面向对象软件设计》中总结出了23种基本的设计模式。四人组由Erich Gamma, Richard Helm, Ralph Johnson, 和John Vlissides组成。实际中有很多面向对象设计模式,但这23种模式被公认为是所有其他设计模式的基础。

Farhana: 我能发明一个新的模式吗?这可能吗?

Shubho: 当然,亲爱的,为什么不能呢?!设计模式不是由科学家发明创造的。它们是被发现找到的。这意味着任何通用问题场景中都有一些好的设计方案在那。如果我们能够指出一个能够解决一个新的设计相关问题的面向对象设计,那么这将会是一个由我们定义的新的设计模式。谁知道呢?!如果我们发现找到一些设计模式,或许将来有一天人们会称我们为二人组,哈哈。

Fahana: :)

我们将如何学习设计模式?

Shubho: 我一直认为例子是学习的最好途径。在我们的学习方法中,我们不会先讨论理论后讨论实现。我认为这是很糟糕的方式。设计模式不是基于理论的发明。事实上,问题场景首先出现,其次是基于这些问题的来龙去脉和需求,然后是一些设计方案的演化,最后其中的一些被标准化为模式。所以对每一个我们讨论的设计模式,我们将尝试理解并分析一些现实生活中的例子,然后一步步尝试归纳一个设计,并最后总结一些与某些模式匹配设计。设计模式就是在这些相似过程中发现的。你认为呢?

Farhana:我想这种方式对我更有用。如果我能通过分析问题和归纳方案得出设计模式,我就不用死记那些设计模式和定义了。请按照你的方式继续。

一个常见的设计问题和它的解决方案

Shubho: 让我们考虑下面的场景:

我们房间里有些电器(电灯,风扇等)。这些设备按照某些方式布局,并由开关控制。任何时候你都能替换或排查一个电器而不用碰到其他东西。例如,你可以换一个电灯而不需要换开关。同样,你可以换一个开关或排查它而不需要碰到或替换相应的电灯或风扇;甚至你可以用把电灯连接到风扇的开关上,把风扇连到电灯的开关上,而不需要碰到开关。

电器:风扇和电灯

风扇和电灯的两种不同开关,一个普通点,另一个别致点

Farhana: 是的,但就是这样子,对吗?

Shubho: 是的,确实如此,就该如此布局。当不同东西联系在一起时,它们应该按照一定方式联系:修改或替换一个系统时不会影响到另一个,或者说即便有,也应该最小化。这能够让你的系统易于管理,且成本低。想想一下,如果改一下房间里的灯同时需要改开关,你会乐意在你房子上花钱并安装这个系统吗?

Farhana: 当然不会。

Shubho: 现在,让我们思考一下电灯或风扇如何连接到开关上才能达到改变一个不会影响到另一个。你认为该如何?

Farhana: 用电线!

Shubho: 很好。把电灯/风扇和开关联系到一起的是电线和电器布局。我们可以它们看做不同系统间相互联系的桥梁。其基本的思想是,一个事物不能和另一外一个事物直接联系。当然啦,它们应当通过某些桥梁或接口联系在一起。用软件术语来说,这叫“松耦合”。

Farhana: 我知道了。

Shubho: 现在,让我们尝试推断在电灯/风扇和开关例子中的几个关键问题,并尝试推断它们是如何设计并联系起来的。

Farhana: 好,我们试一下。

例子中我们有开关,可能有几种开关,如普通的开关,漂亮的开关,但通常来说它们还是开关,并且每种开关都能够打开和关闭。

所以下面我们会有一个开关基类Switch

public class Switch {
public void On() {
//打开开关
}
public void Off() {
//关闭开关
}
}

接下来我们可以有一些具体的开关,例如一个漂亮开关,一个普通开关等等,当然,我们会让类FancySwitchNormalSwitchnd继承类Switch

public class NormalSwitch : Switch {
}
public class FancySwitch : Switch {
}
这里的两个具体类有自己的特征和行为,只是此时此刻,我们简单化以下。

Shubho: 非常棒,接下来电灯和风扇怎么办?

Farhana: 我试试. 根据OODP的开放闭合原则,我们知道只要可能,就应该尝试抽象,对吗?

Shubho: 对

Farhana: 跟开关不一样,风扇和电灯等是两种不同的事物。对于开关,我们能够使用一个开关基类Switch,但风扇和电灯是两个不同的事物,相比定义一个基类,接口可能更合适。一般来说,他们都是电器。所以我们可以定义一个接口,如IElectricalEquipment,作为对电灯和风扇的抽象,可以吗?

Shubho: 可以

Farhana: 好,每种电器都有些相同的功能。他们能够打开和关闭。所以接口可能如下:

public interface IElectricalEquipment {
void PowerOn(); //每种电器都能打开
void PowerOff(); //每种电器都能关闭
}
Shubho: 太好了,你很善于抽象东西。现在我们需要一座桥梁。在现实中,电线是桥梁。在我们对象设计中,开关知道如何打开和关闭电器,电器以某种方式联系到开关。这里我们没有电线,让电器连接到开关的唯一方式是封装。

Farhana: 是的,但开关不能直接知道风扇或电灯。开关应当知道一个电器IElectricalEquipment能够打开或关闭。这意味着,ISwitch应该有一个IElectricalEquipment实例,对吗?

Shubho: 对,对风扇或电灯的封装的实例是一个桥梁。所以让我们修改Switch类以便封装一个电器:

public class Switch {
public IElectricalEquipment equipment {
get;
set;
}
public void On() {
//开关打开
}
public void Off() {
//开关关闭
}
}
Farhana: 明白。让我们定义真实的电器:风扇和电灯。如我所见,一般来说它们都是电器,所以它们都简单实现了IElectricalEquipment接口。

下面是风扇类:

public class Fan : IElectricalEquipment {
public void PowerOn() {
Console.WriteLine(
"风扇打开");
}
public void PowerOff() {
Console.WriteLine(
"风扇关闭");
}
}

下面是电灯类:

public class Light : IElectricalEquipment {
public void PowerOn() {
Console.WriteLine(
"电灯打开");
}
public void PowerOff() {
Console.WriteLine(
"电灯关闭");
}
}

Shubho:太好了。现在让开关工作。当开关打开关闭的时候它应当能够打开关闭电器(它连接到的) 。

这里的关键点是:

  • 当开关按下开时,连接的电器也应该打开。
  • 当开关按下关时,连接的电器也应该关闭。

大致的代码如下:

static void Main(string[] args) {
//构造电器设备:风扇,开关
IElectricalEquipment fan = new Fan();
IElectricalEquipment light
= new Light();

//构造开关
Switch fancySwitch = new FancySwitch();
Switch normalSwitch
= new NormalSwitch();

//把风扇连接到开关
fancySwitch.equipment = fan;

//开关连接到电器,那么当开关打开或关闭时电器应该打开/关闭
fancySwitch.On();
fancySwitch.Off();

//把电灯连接到开关
fancySwitch.equipment = light;
fancySwitch.On();
//打开电灯
fancySwitch.Off(); //关闭电灯
}

Farhana: 明白。开关的On()方法应当内部调用电器的TurnOn()方法,Off()方法应当内部调用TurnOff()方法,所以开关类Switch应如下:

public class Switch {
public IElectricalEquipment equipment {
get;
set;
}
public void On() {
Console.WriteLine(
"开关打开");
equipment.PowerOn();
}
public void Off() {
Console.WriteLine(
"开关关闭");
equipment.PowerOff();
}
}

Shubho: 很好。这自然允许你把风扇从一个开关接到另一个上。不过你看,反过来也可以。这意味着你可以改变风扇或电灯的开关而不需要碰到风扇或电灯。例如,你可以很轻松的把点灯的开关从FancySwitch换到NormalSwitch上,如下:

normalSwitch.equipment = light;
normalSwitch.On();
//打开电灯
normalSwitch.Off(); //关闭电灯
你看,连接一个抽象电器到一个开关(通过封装)能够让你改变开关和电器而不会对对方产生影响。这个设计是优雅的,良好的。四人组为该模式取名为:桥接模式。

Farhana: 太棒了。我想我明白这个了。从根本上说,两个系统不应当直接联系或依赖与对方。 当然,他们应该联系或依赖于抽象(如依赖倒置原则和开放闭合原则所讲),所以他们是松耦合的,因此我们可以在需要时改变我们的实现而不会对系统其他部分产生过多影响。

Shubho: 你理解了,亲爱的.我们看下桥接模式的定义:

"将抽象部分与实现部分分离,使它们都可以独立的变化"

你看我们的实现完美遵循该定义。如果你有一个类设计器(如Visual Studio或其他支持该功能的IDE环境),你会看到类似的如下类图:

桥接模式类图

在这里, Abstraction开关基类Switch RefinedAbstraction 是具体开关类 (FancySwitch, NormalSwitch 等等。)。 Implementor 是电器接口IElectricalEquipmentConcreteImplementorA 和ConcreteImplementorB 是电灯类Light和风扇类Fan

Farhana: 问你个问题,只是好奇啊。如你所说有很多其他的设计模式,为什么你以桥接模式开始呢?有重要原因吗?

Shubho: 这个问题很好。是的,我以桥接模式而不以其他开始是因为一个理由。我认为桥接模式是所有面向对象模式的基础。理由如下:

  • 它教导如何思考抽象,这是面向对象设计模式的关键概念。
  • 它实现了基本的OOD原则。
  • 它容易理解。
  • 如果正确理解该模式,学习其他模式会很容易。

Farhana: 你认为我理解的对吗?

Shubho: 我认为你理解的非常正确。

Farhana: 那么接下来是什么?

Shubho: 通过理解桥接模式,我们仅仅是开始理解设计模式的思想。在我们接下的对话中,我们将会学习其他的设计模式,我希望你不会觉得它们无聊。

Farhana:不会的,相信我。

作者: 倪大虾 发表于 2011-05-30 09:55 原文链接

评论: 13 查看评论 发表评论


最新新闻:
· 苹果推小SIM卡获取更多话语权(2011-05-30 16:44)
· Google Related Links 实验室项目将于 7 月 25 日关闭(2011-05-30 16:42)
· 陈虎:垂直B2C版图今年划定 流量价格是王道(2011-05-30 16:35)
· 好乐买C轮融资:腾讯投5千万美元 已做好上市准备(2011-05-30 16:32)
· Windows Phone芒果的开发幕后(2011-05-30 16:27)

编辑推荐:推荐阅读:dax.net出品的领域驱动设计系列文章

网站导航:博客园首页  我的园子  新闻  闪存  小组  博问  知识库

相关 [妻子 解释 设计模式] 推荐:

如何向妻子解释设计模式

- brett80 - 博客园-首页原创精华区
  自上篇翻译<如何向妻子解释OOD>后收到了很好的反应. 故特继续翻译作者的一文,以飨读者. 在此文中,作者依旧通过与妻子浅显易懂的对话,向读者解释了什么是设计模式. Shubho:通过我们关于面向对象设计原则(OODP,即SOLID原则)的对话,我想你已经对面向对象设计原则(OODP)有了基本的认识.

如何向妻子解释OOD

- Mydreams - 博客园-首页原创精华区
  此文译自CodeProject上一文,该文章在Top Articles上排名第3,读了之后觉得非常好,就翻译出来,供不想读英文的同学参考学习.   作者(Shubho)的妻子(Farhana)打算重新做一名软件工程师(她本来是,后来因为他们孩子出生放弃了),于是作者就试图根据自己在软件开发设计方面的经验帮助她学习面向对象设计(OOD).

Scala设计模式

- - ITeye博客
       我的话: 在国外网站上看到一篇文章,里面详细描述了很多设计模式,并且用Java及Scala两种语言描述,清晰的让我们看到各种常规的设计模式,在Scala中是如何在语言特性层面直接支持的. 基于文章很nice,我利用今天的空闲时间将其翻译,希望大家能一起学习,讨论. 翻译比较倡促,也就两小时左右,有何不当,请在下面留言指出.

Spring设计模式

- - 行业应用 - ITeye博客
springMVC通常采用属性注入的IOC方式和AOP织入方式相结合实现依赖注入 同时使用强制代理方式,代理类或者接口. 这里又涉及到单例模式(注入的类或者接口在容器中只存在一个)、工厂模式(通过反射实现类实例化过程的公用化)、楼上所说的装饰模式属于AOP织入的一部分. 想了解spring先从IOC和AOP开始吧.

“另类” 设计模式

- Sean Lee - 酷壳 - CoolShell.cn
下面这篇文章来自这里:http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign-patterns,这篇文章有点意思了,山寨了我们著名的Design Pattern. 这篇文章并不是很容易翻译,也许我翻译的不好,大家多指正. 另外,这篇文章将失去原有的趣味在于其使用了经典设计模式的单词很相似的单词,一走眼你还以为是正二八经的设计模式.

【译】屏幕设计模式

- 志强 - 所有文章 - UCD大社区
模式是广泛适用的解决一般问题的解决方案. 在开发应用程序过程中,无论是面对抽象还是实际问题,模式都大有用处. 而标准屏幕模式对于优秀的Web程序甚至企业级软件都很有帮助. 摘要/细节(Master/Detail). 摘要/细节模式可横向也可竖向. 该模式是满足用户既停留在一个页面又可浏览多个条目需求的典范.

设计模式之代理模式(Proxy)

- - 博客园_首页
这段时间一直忙于期末考试,好久没来博客园了,现在好了,终于考完了,也该过上正常的日子了. 开学就是大四的学生了,时间过的可是真快啊,转眼间大学四年已经接近尾声了. 回想大学这三年,成绩还可以吧(年级前10%),参加过各种竞赛(acm,数学建模等等),学生会也呆过(打了一年的酱油),好哥们也有那么五六个(希望以后能在一个城市发展,大学期间的宝贵财富啊),另外所谓的大学生创新实践项目也搞了一个(就算开阔一下视野吧,大学能生有什么创新呢.

设计模式原则总结

- - IT技术博客大学习
0、单一职责原则(SRP) 就一个类而言,应该仅有一个引起它变化的原因. 一、”开放-封闭”原则(OCP) 在软件设计模式中,这种不能修改,但可以扩展的思想也是最重要的一种设计原则. 即软件实体(类、模板、函数等等)应该可以扩展,但是不可修改. 【通俗】:设计的时候,时刻考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,我们增加一些类就完事了,原来的代码能不动则不动.

Web应用的缓存设计模式

- - robbin的自言自语
从10年前的2003年开始,在Web应用领域,ORM(对象-关系映射)框架就开始逐渐普及,并且流行开来,其中最广为人知的就是Java的开源ORM框架Hibernate,后来Hibernate也成为了EJB3的实现框架;2005年以后,ORM开始普及到其他编程语言领域,其中最有名气的是Ruby on rails框架的ORM - ActiveRecord.

设计模式和设计原则

- - 编程 - 编程语言 - ITeye博客
26.1  设计模式和设计原则. 26.1.1  设计模式和设计原则的关系. 面向对象的分析设计有很多原则,这些原则大都从思想层面,给我们指出了面向对象分析设计的正确方向,是我们进行面向对象分析设计应该尽力遵守的准则.        而设计模式已经是针对某个场景下某些问题的某个解决方案. 也就是说这些设计原则是思想上的指导,而设计模式是实现上的手段,因此设计模式也是应该遵守这些原则的,换句话说,设计模式就是这些设计原则的一些具体体现.