乐博娱乐»程序人生»我是怎样教媳妇面向工具编程的

我是怎样教媳妇面向工具编程的

来源:oschina 宣布时间:2015-12-12 阅读次数:乐博

 简介

  我老婆 Farhana 想要继续软件乐博娱乐开发生涯(之前因为我们的第一个孩子出生,她不得不放弃)。我已经有了一些软件设计和乐博娱乐开发的经验,所以这几天我就在试着帮助她学习OOD。

  由于我早年在软件乐博娱乐开发的经验,我总是发现无论一个技术问题看上去何等难搞,只要从现实生活的角度去解释或用对话的方式去讨论总能让它变得更简朴。关于OOD,我们已经有了许多结果丰硕的讨论,我觉得有人可能发现这是一个学习OOD有趣的方式,所以我想我应该分享出来。

  下面是我们的谈话步骤:

 话题:介绍面向工具设计

  丈夫:亲爱的,让我们开始学习面向工具设计。你了解面向工具规范,对吗?

  妻子:你是指封装,继续和多态吗?是的,我了解这些规范。

  丈夫:行,我想你已经知道怎么用类和工具了。今天我们来学习面向工具设计。

  妻子:等等。了解面向工具规范劈面向工具编程来说难道不够吗?我的意思是,我能够界说类,封装属性和要领。我能够凭据它们的关系界说类的继续。那另有什么呢?

  丈夫:很好的问题。面向工具规范和面向工具编程完全是两码事。让我展示一个现实生活中的例子来帮助你理解它们。

  我们从牙牙学语起,都是先从字母表学起的,对吧?

  妻子: 嗯。

  丈夫: 好,然后你就能认单词了,还能通过差异的字母拼写出差异的单词来。慢慢的,你能通过一些基本的语法把这些单词串成一句话。为了使句子时态正确且没有语病,你需要用一些介词,连词,等等。。看下面这句话

  "I" (代词) "want" (动词) "to" (介词) "learn" (动词) "OOD" (名词)

  通过把几个单词摆放妥当一句话就好了,然后用个要害词来说明一下这句话的重点。

  妻子: 亲爱的,你闲扯这些到底要说明什么呢

  丈夫: 我说的这个例子跟面向工具规范很类似,面向工具规范为面向工具编程界说了基本的规范,它是面向工具编程的主要思想。面向工具规范好比基本的英语语法,这些语法教会了你怎么用一个个单词拼凑出一句句话来,而面向工具规范教你怎么用类,怎么把一些属性和要领封装在一个类里,怎么串出类之间的继续关系。

  妻子: 啊哈,我知道了,那么,面向工具适用于哪里呢。

  丈夫: 听我慢慢道来。现在,假设你想写点有内容有题材的文章。你虽然还希望写点你比力擅长的题材的书,就会简朴造几个句子是远远不够的,对吧。你需要笔耕不辍写出一些长篇大论,你还需要学习怎么可以让读者很容易就看懂你写的这些长篇大论。。。

  妻子:嗯,有那么点意思。。。继续吧

  丈夫:现在,如果你想写本关于面向工具设计的书,你需要把这个大的课题拆分成一些小题目。把这些小题目分几个章节写,还得写前言,简介,说明,举例,一篇里另有许多段落。你需要设计一整本书,还得练习一些写作技巧,让文章读起来浅显易懂。这就是综观全局。

  在软件乐博娱乐开发中,OOD就是用来解决从全局出发考虑问题,在设计软件的时候,类和代码可以模块化,可重复使用,可灵活应用,现在已经有许多前人总结出的类和工具的设计原理了,我们直接拿来用就行了,总之,历史的车轮已经碾压出一条清晰的车轮印,我们只要照着走就可以了。

  妻子: 哎,懂了点皮毛,另有许多要学呢。

  丈夫:不用担忧,你很快就会上手的,让我们接着来吧。

 话题:为什么要进行面向工具设计?

  作者:有个很重要的问题,既然我们能够很快的创建几个类,编写程序并提交,为什么我们还要关注面向工具设计?这样不够么?

  妻子:恩,以前我不知道面向工具设计,我也能乐博娱乐开发提交项目。有什么关系?

  丈夫:好吧,先让我给你看一个经典的引述:

"需求稳定的程序乐博娱乐开发会同行走在冰上一样简朴。"

- Edward V. Berard

  妻子:你是指软件乐博娱乐开发说明书会被不停修改?

  丈夫:很是正确!软件乐博娱乐开发唯一的真理是“软件一定修改”。为什么?

  要知道,你的软件解决的是现实世界中的问题,而现实生活不是一成稳定的。

  可能你的软件现在运行良好。但它能灵活的支持“变化”吗?如果不能,那它就不是一个敏捷设计的软件。

  妻子:好,那你就解释一下什么叫做“敏捷设计的软件”!

  丈夫:“一个敏捷设计的软件能轻松应对变化,能被扩展和复用。”

  而应用“面向工具设计”是做到敏捷设计的要害。那么,什么时候你可以说你的程序应用了面向工具设计?

  妻子:我也正想问呢。

  丈夫:如果代码切合以下几点,那么你就在“面向工具设计”:

  • 面向工具
  • 复用
  • 变化的价钱极小
  • 无需改代码即可扩展

  妻子:然后呢?

  丈夫:不只我们。许多人也花了许多时间和精力思考这个问题上,他们实验更好的进行“面向工具设计”,并为“面向工具设计”指出几条基本的原则(你可以用在你的“面向工具设计”中)。他们也确实总结出了一些通用的设计模式(基于基本的原则)。

  妻子:你能说出一些吗?

  丈夫:没问题。现在有许多设计原则,但是最基本的,就是SOLID(缩写),这五项原则。(谢谢鲍勃叔叔,伟大OOD导师)。

  S  = 单一责任原则
  O = 开闭原则
  L  = Liscov替换原则
  I  = 接口隔离原则
  D = 依赖倒置原则

  在下面的讨论中,我们将详细了解这些。

 话题:单一功效原则

  作者:让我们先来看图,我们应该谢谢制作这张图的人,因为它们真的太有趣了。

乐博

单一功效原则图

  它的意思是:“如果你可以在一个设备中实现所有的功效,你却不能这样做”。为什么呢?因为从久远来看它增加了许多的可治理性问题。

  从面向工具角度解释是:

  "导致类变化的因素永远不要多于一个。"

  或者换行个说法:"一个类有且只有一个职责"。

  妻子:可以解释一下么?

  丈夫:虽然,这个原则是说,如果有多于一个原因会导致你的类改变(或者它的职责多余一个),你就需要凭据其职责把这个类拆分为多个类。

  妻子:嗯...这是不是意味着在一个类里不能有多个要领?

  丈夫:虽然不是。你虽然可以在一个类中包罗多个要领。问题是,他们都是为了一个目的。那么,为什么拆分很重要的?

  那是因为:

  • 每个职责都是轴向变化;
  • 如果类包罗多个职责,代码会变得耦合;

  妻子:给个例子呗?

  丈夫:木有问题啊,瞅瞅下面类的结构。其实,这个例子是 Bob 叔叔那儿来的,得谢谢他。

违反SRP原则的类条理结构

  这里,Rectangle 类干了下面两件事:

  • 盘算矩形面积;
  • 在界面上绘制矩形;

  而且,有两个程序使用了 Rectangle 类:

  • 盘算几何应用程序用这个类盘算面积;
  • 图形程序用这个类在界面上绘制矩形;

  这违反了SRP原则(单一职责原则)!

  妻子:肿么回事?

  丈夫:你瞅瞅,Rectangle 类干了俩不相干的事。一个要领它盘算了面积,另外一个它返回一个体现矩形的 GUI 资源。这问题就有点乐了:

  • 在盘算几何应用程序里咱得包着 GUI。就是说,写几何应用代码,咱也得引用 GUI 库;
  • 要是为了图形应用所改变 Rectangle 类,盘算几何应用也可能随着变,然后还得编译,还得测试,另一边也是;

  妻子:是很乐。就是说,咱得凭据类的职责离开写呗?

  丈夫:必须滴。猜猜怎么干?

  妻子:我想想,我寻思这得这么办:

  我瞅着得按职责拆成两个类:

  • Rectangle:这个类界说 Area() 要领;
  • RectangleUI:这个把 Rectangle 类继续过来,界说 Draw() 要领。

  丈夫:很好。这么个,盘算几何应用使 Rectangle 类,图形应用使 RectangleUI 类。咱还可以把这俩类分到俩单独的 DLL 中,然后改的时候就不用管另一个了。

  妻子:谢了,我或许明白 SRP 原则了一句话:SPR 就是把工具分到不能再分了,再集中化治理和复用。囔,在要领层面上,咱不也得用 SPR 原则?我是说,咱写的要领里有许多干差异事儿的代码,这也不切合 SPR原则吧。

  丈夫:你说地不差。要领也得离开,一个要领干一个活。这么着你复用要领,要是改了,也不用改太多。

 话题:开闭原则

  作者:“开闭原则“图示如下:

乐博

图:开闭原则图

  让我来解释一下,设计规则如下:

  “软件实体(类,模块,函数等)应该对扩展开放,对修改关闭。”

  这意味着在最基本的层面上,你可以扩展一个类的行为,而无需修改。这就像我能够穿上衣服,而对我的身体不做任何改变,哈哈。

  妻子: 太有意思啦. 你可以通过穿差异的衣服来改变你的外貌, 但是你不必为此改变自己的身体.所以你是对扩展开放的, 对吧?

  丈夫: 是的. 在面向工具设计中, 对扩展开放意味着模块/类的行为可以被扩展,那么当需求变化时我们可以用林林总总的要领制定功效来满足需求变换或者新需求

  妻子: 除此之外你的身体是对修改关闭的. 我喜欢这个例子. 所以, 对于焦点模块或类的代码在需要扩展的时候不应该被修改. 你能结合具体例子解释下吗?

  丈夫: 虽然了, 先看下面的例子.这个就不支持 "开放-关闭" 原则:

乐博

  类的条理结构已经讲明了这是违反"开放-关闭"原则的.

  你看, 客户端类和服务端类都是具体的实现类. 因为, 如果某些原因导致服务端实现改变了, 客户端也需要相应变化.

  妻子: 有原理. 如果一个浏览器的实现和一个指定的服务器(好比IIS)紧紧的耦合在一起, 那么如果服务器由于某种原因替换成了另外的(好比, Apache) 浏览器也需要做相应的变化或者被替换掉. 何等恐怖的一件事啊!

  丈夫: 很是正确. 因为下面的将是一种好的设计方案:

乐博

  类的条理关系展示了"开放-关闭"原则

  在这个例子中, 添加了一个抽象的Server类, 而且客户端保持了抽象类的引用, 具体的Server类实现了这个抽象Server类. 所以, 由于某种原因Server的实现类发生了改变, 客户端不需要做任何改变.

  这里的抽象的Server类对修改关闭, 具体的Server实现类对扩展开放.

  妻子: 我的理解是, 抽象是要害, 对吗?

  丈夫: 是的, 基本上, 你要对系统的焦点业务进行抽象, 如果你抽象化做的比力好, 很可能, 在扩展功效的时候它们不必做任何改变 (好比Server就是一个抽象的看法).  你所界说的抽象的实现 (好比, IIS服务器 实现了 Server) 和 抽象的代码 (Server) 要尽可能的多. 这样在客户端代码中不需要做任何修改就会允许你界说一个新的实现(好比, ApacheServer) .

 主题: 里氏替换原则

  丈夫: "里氏替换原则"听起来很是的庞大,但是设计思想却是很是基础的. 看下面这个有趣的海报

乐博

里氏替换原则海报

  原则描述了:

  "子类型必须能够替换它们的基类."

  或者, 换句话说:

  "使用基类引用的函数必须能够使用派生类而无须了解派生类."

  妻子: 对不起, 这听起来让我觉得有点乱. 我认为这个是面向工具编程的基本原则. 这个叫做多态性, 对吧? 为什么面向工具设计原则需要考虑这个问题?

  丈夫: 很是好的问题. 这有一些答案:

  在基本的面向工具原则中, "继续" 通常被描述成 "is a" 的关系. 如果一个 "乐博娱乐开发者" 是"软件专业人员", 那么 "乐博娱乐开发者" 类 应该 继续 "软件乐博娱乐开发人员" 类. 这样的 "Is a" 关系 在类设计阶段很是重要, 但是这也很容易让设计者自得忘形从而以一个糟糕的继续设计告终.

  "里氏替换原则" 仅仅是一种确保继续被正确使用的手段.

  妻子:我明白了。真有趣。

  丈夫:是的,亲爱的,确实如此。让我们来看看一个例子:

乐博

  类条理结构图展示的是一个Liskov替换原则的例子.因为 KingFisher类拓展(继续)了Bird类,因此继续了Fly()这个要领,这是很是不错的.

  我们再来看看下面的例子

乐博

修正过的Liskov替换原则的类条理结构图

  Ostrich(鸵鸟)是一种鸟(显然是),并继续了 Bird 类。但它能飞吗?不能,这个设计就违反了里氏替换原则。

  因此,纵然在现实中看上去没什么问题,在类设计中,Ostrich 都不应该继续 Bird 类,而应该从 Bird 中分出一个不会飞的类,由 Ostrich 继续。

  妻子:好吧,明白了。我说说为什么里氏替换原则如此重要:

  • 如果不遵循 LSP原则,类继续就会杂乱。如果子类实例被作为参数通报给要领,结果难以预测。
  • 如果不遵循 LSP原则,基于父类编写的单元测试代码将无法成功运行子类。

  我说的对吗?

  作者:完全正确,你可以设计一个工具并用LSP作为验证工具来测试该工具是否能够继续。

 话题:接口隔离原则

  作者:今天我们讲下“接口隔离原则”,看看下面这张海报

乐博

接口隔离原则海报

  妻子:这是什么意思?

  作者:它的意思是这样的:“用户不应该被迫依赖他们不使用的接口。”

  妻子:解释一下。

  作者:好吧,解释如下:

  假设你想去买一台电视机而且有两种类型可以选择,其中一种有许多开关和按钮,但是多数对你来说用不到,另一种只有几个开关和按钮,而且看来你很熟悉怎么用。如果这两种电视机提供同样的功效,你会选择哪一种?

  妻子:虽然是第二种了。

  作者:嗯,但是为什么呢?

  妻子:因为我不需要看起来很麻烦而且对我也不须要的开关和按钮。

  丈夫:正确。同样的,如果你有一些类,你通过接口袒露了类的功效,这样外部就能够知道类中可用的功效,客户端也可以凭据接口来设计。虽然那,如果接口太大,或是袒露的要领太多,从外部看也会很杂乱。接口包罗的要领太多也会降低可复用性, 这种包罗无用要领的”胖接口“无疑会增加类的耦合。

  这还会引起其他的问题。如果一个类视图实现接口,它需要实现接口中所有的要领,哪怕一点都用不到。所以,这样会增加系统庞大度,降低系统可维护性和稳定性。

  接口隔离原则确保接口实现自己的职责,且清晰明确,易于理解,具有可复用性。

  妻子:我明白了,你的意思是接口只应该包罗须要的要领而不是所有的。

  作者:是的,让我们看一个例子。

  下面的接口是一个“胖接口”,这违反接口隔离原则:

乐博

违反接口隔离原则的接口示例

  注意,IBird接口界说 Fly()的行为有许多鸟类的行为。现在,如果一只鸟类(比方说,鸵鸟)实现了这个接口,它将会实现不须要的Fly()的行为(鸵鸟不会飞)。

  妻子:是啊。因此,这个接口必须被支解?

  作者:是的,“胖接口”应该离开成两个差异的接口,IBird 和IFlyingBird,而IFlyingBird继续于IBird。

乐博

接口隔离原则的例子中正确版本的接口

  如果有一只不会飞的鸟(好比,驼鸟),只要用IBird接口即可,如果有一保会飞的鸟(好比,翠鸟),只要用IFlyingBird接口即可。

  妻子:所以,回过头来看有许多按钮开关的电视的例子,制造商应该有电视机的图纸,开关和按钮也在这个方案里。若他们想造一台新款电视机时想要复用这张图纸,他们必须添加更多的按钮和开关,否则没法复用,对么?

  丈夫:对。

  妻子:若是他们真的想要复用这个方案,他们应该将电视机的图纸分为更小的部门,才气在以后制造新款电视机的时候复用这些设计方案。

  丈夫:你理解了。

 话题:依赖倒置原则

  作者:这是SOLID原则中最后的原则。图示如下:

乐博

依赖倒置原则图示

  它的意思是:

  “高条理的模块不应该依赖于低条理的模块,而是,都应该依赖于抽象。”

  作者:我们用一个现实的例子来理解。你的汽车是用许多部件组成,好比发动机,车轮,空和谐其他的部件,是吧?

  妻子:是啊,虽然是这样。

  丈夫:你看,它们并没有严格的构建在一个部件里;就是说,它们都是“插件”,要是引擎或着车轮出了问题,你可以单独修理它,甚至换一个用。

  替换时,你只需要保证迷恋切合汽车的设计(汽车能使用任何1500CC的引擎或任何18寸的车轮)。

  虽然,你可以在1500CC 的位置上安装2000 CC的引擎,对某些制造商都一样(丰田汽车)。

  可如果你的汽车部件不是“可拔插”的呢?

  妻子:那太可怕了!这样的话,要是汽车引擎故障,你得整车修理,或者买一辆新车!

  丈夫:是的,那么怎么做到"可插拔"呢?

  妻子:要害是”抽象“,是吧?

  丈夫:对。现实世界中,汽车是高层级的模块/实体,它依赖于底层级的模块/实体,例如引擎和轮子。

  相较于直接依赖于实体的引擎或轮子,汽车应该依赖于抽象的引擎或轮子的规格,这样只要是切合这个抽象规格的引擎或轮子,都可以装到车里跑。

  来看看下面的图:

乐博

依赖倒置原则的类条理结构

  丈夫:注意上面的 Car类,它有两个属性,且都是抽象类型(接口)而非实体的。

  引擎和车轮是可插拔的,这样汽车能接受任何实现了声明接口的工具,且 Car 类无需任何改动。

  妻子:所以,如果代码不遵循依赖倒置,就有下面的风险:

  • 使用低层级类会破环高层级代码;
  • 当低层级的类变化时,需要太多时间和价钱来修改高层级代码;
  • 代码可复用性不高

  丈夫:亲爱的,你说到点子上了!

 总结

  丈夫:除 SOLID 原则外另有许多此外面向工具原则。好比:

  • “组合替代继续”:是说“用组合比用继续好”;
  • “笛米特规则”:是说“类对其它类知道的越少越好”;
  • “配合关闭原则”:是说“相关类应该一起打包”;
  • “稳定抽象原则”:这是说"类越稳定,就越应该是抽象类";

  妻子:我得学习这些原则吗?

  丈夫:虽然了。你可以在网上学习。Google 它,学习它,理解它。有问题就找我。

  妻子:我听说另有些凭据设计原则编写的设计模式。

  丈夫:对的。设计模式不外就是针对一些经常泛起的场景的一些通用的设计建议。主要的想法照旧面向工具原则。你可以认为设计模式是“框架”,OOD 原则是“规范”。

  妻子:那么之后我将学习设计模式是吧?

  丈夫:是的,亲爱的。

  妻子:应该会很有意思。

  丈夫:必须地!

  原文地址:http://www.codeproject.com