英文原文:How I explained Design Patterns to my wife: Part 1
引子
我跟媳妇曾经就面向对象设计这个话题做过有趣的探讨。当我把它们发表在社区之后,得到了一些很不错的反馈,也大大鼓舞了我。所以,我很高兴能把我们后面的一次谈话继续分享出来,那是关于面向对象的设计模式的,大家往下看吧。
什么是设计模式
丈夫: 我想你现在对面向对象的设计原则有了一些基本概念了吧。我们那次关于OOD原则(SOLID原则)的有趣谈话被我发表在社区上了,你不会介意吧?网址在这里: 我怎么向妻子解释OOD。
设计模式则是这些原则在某些特定和常用条件下的应用,并且做了一些标准化。我们还是来一些例子吧。
媳妇: 好极了,我喜欢例子。
丈夫:以我们的车为例吧。它是一个对象,不过有点复杂,是由几千个其它对象组成的,包括引擎、车轮、转向装置、座位、车身,等等。
一辆车的各种零件。这辆车在制造的时候,制造商收集所有的零件,把它们组装起来。这些零件本身也是复杂的对象,是由其它的制造商组装的。但汽车公司并不关心这些零件是怎么造 出来的(当然,他们需要确信这些零件的质量是过硬的)。他们只会关心如何通过不同的方式将不同的零件组装起来,以便生产出不同型号的汽车。
由不同零件根据不同设计组装成的不同型号的车。
媳妇:每种型号的汽车应该都有各自的设计和蓝图什么的,是吧?
丈夫:非常正确。而且,这些设计是经过深思熟虑的,花了很长的时间和很大的努力才得以诞生。完成设计之后,汽车的生产就只剩下遵循设计这么简单的事了。
媳妇: 嗯……很不错的办法,先想出一些优秀的设计,然后遵照这些设计,就可以在很短的时间里造出不同的东西。如果制造商想要开发某种型号的产品,不需要从头进行设计,或者说重新造轮子,只要遵循那些设计就可以了。
用于不同型号产品(车)的不同设计。
丈夫:你说到点子上了。现在,回到现实里,我们是软件厂商,我们需要根据需求,用不同的组件来创造出不同的软件。在这个过程中,一定会碰到一些情形,是在许多不同的软件里都有的,对不对?
媳妇:对啊。而且,我们还常常在不同的软件里碰到相同的设计难题呢。
丈夫: 我们尝试着用一种面向对象的方式来开发我们的软件,利用OOD原则来让我们的代码更容易管理、重用和扩展。就像你上面提到的那些相同的问题,如果我们预先就有一些良好的设计,那是不是很棒呢?
媳妇: 是啊,那可以省下大把的时间,而且这样打造的软件质量更好,更容易管理。
丈夫: 没错。还有个好消息,我们并不需要自己造轮子。这么多年以来,遭遇同样问题的人们早已发现了许多很棒的解决方案,而且把它们标准化过了。我们管这些方案叫设计模式。
我们要感谢四人帮(GoF),他们在设计模式: 可重用面向对象软件的基本元素这本书里归纳了23个最基本的设计模式。想知道这四个牛人是谁吗?Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides。面向对象的设计模式很多,但大家认为这23个模式是其它模式的基础。
媳妇:我能创建一个新模式吗?有可能吗?
丈夫:当然可以,亲爱的,为什么不行呢?设计模式并不是被科学家发明和创造的东西。他们只是被“发现”而已。 也就是说,对任何一个普通的问题场景,肯定会有一些好的设计方案。如果我们能识别出一个能解决某个新问题的面向对象设计,那我们就定义了一个新的设计模式。谁知道呢?如果我们发现一些设计模式,没准儿大家会叫我们“二人帮”呢...哈哈。
媳妇::)
我们怎么来学习设计模式呢?
丈夫: 我始终坚信,通过例子学习是最好的。在我们的学习过程中,我们不会“先理论后实践”,因为我认为这是一种“坏”方法。设计模式不是基于理论发明的。相反,总是先有问题场景,再基于需求和情景不断演化设计方案,最后把一些方案标准化成“模式”。所以,我们讨论每一个设计模式时,要尽量用生活中的真实问题来理解和分析。然后尝试一步步地阐述设计,并以一个能匹配某些模式的设计收尾。设计模式就是这样被发现的,你觉得呢?
媳妇: 我觉得对我来说,这种方式可能更好使。如果我能通过先分析问题,然后阐述解决方案, 最后得到一个设计模式,我就不用死记那些图形和定义了。就这么办吧。
基础的设计问题和解决方案
丈夫:让我们考虑一下下面的情况:
我们的家里都有家用电器(比如电灯和风扇),他们都是由开关控制。 任何时候,你都可以在不改变其他东西的情况下做一些事。你可以在不更换开关的情况下换掉灯泡,也可以在不接触灯泡或者风扇的情况下更换开关,甚至可以在不接触开关的情况下,把灯泡和风扇的开关互换。
家用电器:风扇和灯泡
两种开关(第二个显然比第一个要好看)
媳妇:对啊,这看起来很自然,不是吗?
丈夫:是的,非常自然,同时也应该这样安排。当不同的事物联系到一起时,他们应该在一个可以变更或者可以替换 的系统中以便不相互影响,或者影响尽可能的小。这样让你更为方便、成本最小地去管理你的系统。可以想象,如果你要换一个你房间里的灯泡得要求你把开关也换了,你会考虑在你房子里使用这样的一个系统吗?
媳妇:当然不会。
丈夫:现在,让我们想一下电灯或者电风扇是怎样和开关联系起来以便更换其中一个而不会影响到其他的。你想到什么了?
媳妇:当然是电线啦。
丈夫:正确,是电线以及其他的电工手段把电灯/电风扇与开关连接起来。我们可以把这概括为沟通不同系统的桥梁。基本思想是,一个事物不能直接连接另一个事物。当然,他们能够通过一些桥梁或接口连接起来。在软件世界里,我们称之为“松耦合”。
媳妇:嗯,我明白这点。
丈夫:现在,我们来尝试理解一些类似电灯/电风扇与开关类似的关键问题,同时尝试理解是怎样设计和关联它们的。
媳妇:好的,我们开始吧。
在我们的列子里,有一些开关,这些类似普通的开关、有不同的花式开关可能有不同的种类,但是,一般情况下,他们就是开关。同时,每个开关都能开和关。
这样的话,我们就会得到如下的Switch基类:
01 |
public class Switch |
02 |
{ |
03 |
public void On() |
04 |
{ |
05 |
//Switch has an on button |
06 |
} |
07 |
public void Off() |
08 |
{ |
09 |
//Switch has an off button |
10 |
} |
11 |
} |
同时,我们可能也 需要一些特定类型的开关,譬如正常的开关、不同花式的开关等等。同样的我们扩展Switch类来实现FancySwitch和NormalSwitch:
1 |
public class NormalSwitch : Switch |
2 |
{ |
3 |
} |
4
5 |
public class FancySwitch : Switch |
6 |
{ |
7 |
} |
这两个特定的开关类可能用于它们自己特有的行为和特征,但是到目前为止,我们还是保持它们现在的简单形式。
丈夫:棒极了。现在,如何处理风扇和灯呢?
媳妇:让我试试。按照面向对象设计原则中的封闭原则,我认为我们需要试着在任何可能的地方做抽象处理,对吗?
丈夫:对。
媳妇: 电扇和电灯情况有点不一样,它们两个不是同一种东西。对于不同的开关,我们可以用同一个基本的Switch类,但对于电扇和电灯就不大合适了,感觉用接口会更合适一点。因为,从大体上讲,它们都算是电器,那么我们可以就定义一个接口: IElectricalEquipment,用它来抽象电扇和电灯,对不对?
丈夫: 很对。
媳妇: 那么,所有电器都有一些共性,可以被打开和关闭。那么这个接口就可以是:
1 |
public interface IElectricalEquipment |
2 |
{ |
3 |
void PowerOn(); //Each electrical equipment can be turned on |
4 |
void PowerOff(); //Each electrical equipment can be turned off |
5 |
} |
丈夫: 没错。你的抽象能力很强呀,媳妇。好了,现在我们还缺一座桥。在现实世界里,桥是电线。在对象的世界里,开关知道怎么开关电器,电器需要用某种方式跟开关连起来。可这里没有电线,我们唯一有的,是封装。
媳妇: 没错,开关并不知道电扇和电灯的存在。它只知道它可以打开或关闭某个电器IElectricalEquipment。那么,也就是说每个Switch应该拥有一个IElectricalEquipment实例,是吧?
丈夫: 很对。这里,被封装的实例,也就是IElectricalEquipment,就是这座桥。好,我们来修改一下Switch类,让它把电器封装进去:
01 |
public class Switch |
02 |
{ |
03 |
public IElectricalEquipment equipment |
04 |
{ |
05 |
get; |
06 |
set; |
07 |
} |
08 |
public void On() |
09 |
{ |
10 |
//Switch has an on button |
11 |
} |
12 |
public void Off() |
13 |
{ |
14 |
//Switch has an off button |
15 |
} |
16 |
} |
媳妇: 我懂了。接下来我再定义真正的电器吧。电扇和电灯,总体上都是电器,所以它们应该实现IElectricalEquipment接口。
电扇类:
01 |
public class Fan : IElectricalEquipment |
02 |
{ |
03 |
public void PowerOn() |
04 |
{ |
05 |
Console.WriteLine("Fan is on"); |
06 |
} |
07 |
public void PowerOff() |
08 |
{ |
09 |
Console.WriteLine("Fan is off"); |
10 |
} |
11 |
} |
电灯类:
01 |
public class Light : IElectricalEquipment |
02 |
{ |
03 |
public void PowerOn() |
04 |
{ |
05 |
Console.WriteLine("Light is on"); |
06 |
} |
07 |
public void PowerOff() |
08 |
{ |
09 |
Console.WriteLine("Light is off"); |
10 |
} |
11 |
} |
丈夫: 很好。现在该是接上开关的时候了。开关在打开和关闭的时候,必须能打开和关闭它所连接的电器。
也就是说:
- 当按下开关的打开按钮时,必须打开连接的电器。
- 当按下开关的关闭按钮时,必须关闭连接的电器。
我们想要的功能基本上是这个样子:
01 |
static void Main(string[] args) |
02 |
{ |
03 |
//We have some electrical equipments, say Fan, Light etc. |
04 |
//So, lets create them first. |
05
06 |
IElectricalEquipment fan = new Fan(); |
07 |
IElectricalEquipment light = new Light(); |
08
09 |
//We also have some switches. Lets create them too. |
10
11 |
Switch fancySwitch = new FancySwitch(); |
12 |
Switch normalSwitch = new NormalSwitch(); |
13
14 |
//Lets connect the Fan to the fancy switch |
15
16 |
fancySwitch.equipment = fan; |
17
18 |
//As the switch now has an equipment (Fan), |
19 |
//so switching on or off should |
20 |
//turn on or off the electrical equipment |
21
22 |
fancySwitch.On(); //It should turn on the Fan. |
23
24 |
//so, inside the On() method of Switch, |
25 |
//we must turn on the electrical equipment. |
26
27 |
//It should turn off the Fan. So, inside the On() method of |
28 |
fancySwitch.Off(); |
29 |
//Switch, we must turn off the electrical equipment |
30
31 |
//Now, lets plug the light to the fancy switch |
32
33 |
fancySwitch.equipment = light; |
34 |
fancySwitch.On(); //It should turn on the Light now |
35 |
fancySwitch.Off(); //It should be turn off the Light now |
36 |
} |
媳妇:明白了。那么,开关的On()方法应该调用电器的TurnOn()方法,而它的Off()方法应该调用电器的TurnOff()方法,Switch类应该是这个样子:
01 |
public class Switch |
02 |
{ |
03 |
public void On() |
04 |
{ |
05 |
Console.WriteLine("Switch on the equipment"); |
06 |
equipment.PowerOn(); |
07 |
} |
08 |
public void Off() |
09 |
{ |
10 |
Console.WriteLine("Switch off the equipment"); |
11 |
equipment.PowerOff(); |
12 |
} |
13 |
} |
丈夫: 干得好。这个电扇显示是可以换开关的。而且,反过来也是可以换的,可以不修改电扇和电灯,直接更换开关,例如,我们可以把电灯的开关从FancySwitch换成NormalSwitch:
1 |
normalSwitch .equipment = light; |
2 |
normalSwitch.On(); //It should turn on the Light now |
3 |
normalSwitch.Off(); //It should be turn off the Light now |
看到没,我们可以在不影响任何一方的情况下,改变另一方。这个设计看起来很不错,而且相当的优雅。其实四人帮管这个设计叫桥梁模式。
媳妇:酷!我懂了。一般来说,两个系统不应该直接地互相联接和依赖。相反,他们应该通过抽象来联接或依赖(正如依赖倒置和开闭原则所言),这样它们就是松耦合的,我们就可以在必要时轻松地修改实现,而不对系统的其它部分造成太大影响。
我:亲爱的,你得我真传了。我们来看一下桥接模式的定义吧:
“把抽象和实现解耦,使得它们可以独立地变化”
我们的设计完全符合定义。如果你有类设计器(Visual Studio和其它流行的IDE都有这功能),你能看到一个和下图相似的类图:
在我们的例子里,Abstraction是基础的Switch类,RefinedAbstraction是某个具体的开关类(FancySwitch和NormalSwitch),Implementor是IElectricalEquipment接口,ConcreteImplementorA和ConcreteImplementorB分别是Fan和Light类。
媳妇: 我有点好奇。你不是说有很多模式么,干嘛先说桥梁模式呢?有什么特别重要的原因吗?
丈夫: 问得好。我从桥梁模式开始,而不是其它模式,只有一个原因。我觉得它是所有面向对象设计模式的基础。因为:
- 它能教你如何抽象地思维,这可是OO设计模式的关键。
- 它实现了基本的OOD原则。
- 它很好理解。
- 如果你能正确地理解它,学习其它模式就易如反掌了。
媳妇: 你觉得我理解的对么? 丈夫: 哦~亲爱的,我觉得你理解的非常好。 媳妇: 那么,下一步呢? 丈夫:通过了解桥接模式,我们才稍微的了解了设计模式的概念。在我们接下来的谈话中,我们将会学习其他的涉及模式,希望你不会觉得它们无聊。 媳妇: 不会的,相信我。 Next 请关注我们的下一次谈话:) |
From <http://www.oschina.net/translate/how-i-explained-design-patterns-to-my-wife-part-1?from=20130113>
相关推荐
"我如何向妻子解释设计模式:第1部分"这个标题采用了一种轻松幽默的方式,旨在让非专业人士也能理解这些复杂的概念。通过描述中的“有趣的对话”方式,我们可以期待内容将复杂的技术知识转化为易于理解的日常交流。 ...
行为型设计模式关注对象之间的通信,包括模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式和访问者模式。这些模式关注于处理复杂的控制流,使...
模板模式是一种行为设计模式,它使你能在不破坏封装性的前提下,定义一系列算法的族,每一种算法都由一个方法来实现。这种类型的设计模式属于行为模式。 模板模式的关键在于一个抽象类(或接口)定义了算法的主要...
好玩哈送媳妇的.rar
【大班语言教案:老鼠娶媳妇】是一份针对幼儿园大班儿童设计的语言教育活动方案,旨在通过生动有趣的故事和互动游戏,提升孩子们的语言表达能力和理解能力。这份教案以中国传统故事《老鼠娶媳妇》为背景,结合童谣的...
首先,孝道是中国传统文化中的核心价值之一,这两位好媳妇通过日常生活中无微不至的照顾,如精心准备饭菜、洗衣打扫,甚至在婆婆病重时守护在侧,充分体现了孝道精神。在IT行业中,这种关注细节、无私奉献的精神可以...
2. 第一件事:小女孩迷路时,一对夫妇帮助她找到家。第二件事:十几岁的“我”搬运粮食时,一位老人助力。第三件事:女儿出于关心打碎杯子,却惹怒了病中的“我”,然后诚恳道歉。 3. 可以拟标题为“温暖的瞬间”...
幼儿园教案2021-大班语言教案:老鼠娶媳妇.doc
最美好媳妇事迹材料优秀范文(15篇).pdf
这篇文档实际上并不是关于IT行业的知识,而是一篇个人情感表达的检讨书,内容涉及到作者对伴侣的深情告白和自我反省。不过,我们可以从中提取一些普遍的情感管理和沟通技巧,这些在人际交往中,包括IT行业的团队合作...
从这部分内容来看,我们可以提取出以下IT知识相关的知识点: 1. **文本编辑工具**: 文件格式是.docx,表明这份祝寿词是使用Microsoft Word或其他兼容的文档编辑软件创建的,例如Google Docs或LibreOffice Writer。 ...
【文档标题】与【描述】提到的是一个关于“好媳妇”的事迹材料,主要讲述了主人公xx作为一名乡村教师,如何在生活中践行孝道,关爱家人,并在工作中展现出敬业精神的故事。以下是相关知识点的详细阐述: 1. 孝道...
【文档描述】:“祝福媳妇生日快乐句子精选.doc”是一个包含了各种表达对妻子生日祝福的句子的资料集合,旨在为人们提供创意和灵感,帮助他们在妻子生日当天送上温馨的祝福。 【标签】:“资料” 【文档部分内容...
描述部分继续阐述了媳妇网购这一行为如何成为婆媳矛盾的触发点。在这里,我们可以看到两种截然不同的应对方式对家庭和谐的影响。一种是以否定和责备的态度面对媳妇的网购行为,另一种则是采取更为开放和理解的态度。...
1. 文章分析技巧:通过阅读《巧媳妇搬石头》的读后感,我们可以学习如何分析和解读文学作品。作者对文章的精炼语言、深刻主题和巧妙构思的赞赏,提示我们在分析文本时应注意文学元素,如修辞手法、人物塑造和情节...
这封信虽然并非关于IT行业的文章,但它涉及的是家庭关系中的代际沟通和相互尊重问题,这在现代社会中也是一个重要的话题。在这个信息化时代,人们更加重视个人权益和情感表达,即使是家庭内部,也需要建立基于理解和...
1. 教案设计:本教案是针对小学四年级语文上册第一单元的《绿叶的梦》课程,旨在通过教学帮助学生理解课文内容,掌握阅读技巧,激发学生的阅读兴趣。 2. 教学内容:《绿叶的梦》是一篇描绘作者童年回忆的文章,主要...