《面向对象设计实践指南:Ruby语言描述》—第1章 1.1节设计赞歌

  1. 云栖社区>
  2. 博客>
  3. 正文

《面向对象设计实践指南:Ruby语言描述》—第1章 1.1节设计赞歌

异步社区 2017-05-02 14:57:00 浏览1332
展开阅读全文

本节书摘来自异步社区《面向对象设计实践指南:Ruby语言描述》一书中的第1章,第1.1节设计赞歌,作者【美】Sandi Metz,更多章节内容可以访问云栖社区“异步社区”公众号查看。

第1章 面向对象设计
面向对象设计实践指南:Ruby语言描述

世界是过程式的。时间不停在向前流动,而事件也一个接一个地逝去。你每天早上的过程或许就是:起床、刷牙、煮咖啡、穿衣,然后上班。这些活动都可以使用过程软件来建模。因为了解事件的顺序,所以你可以编写代码来完成每一件事情,然后仔细地将这些事情一个接一个地串在一起。

世界也是面向对象的。与你互动的对象可能包括有你的老伴和猫,或者是车库里的旧汽车和一大堆的自行车零件,又或者是你的那颗扑通跳动的心脏,以及用来保持健康的锻炼计划。在这些对象中,每一个都拥有它们自己的行为,而且它们之间某些的交互可能还是可预测的。例如,你的老伴意外地把猫给踩了一下,从而引起一个激烈的反应,大家的心跳频率都迅速升高,同时也为你增添了一种新的锻炼方式,这是完全可能的。

在由对象构成的世界里,新的行为编排在很自然的情况下便会出现。你不用显式地为“老伴踩猫”这一流程编写代码,而你所需要的是这样两个对象:一个会抬脚的老伴和一只不喜欢被踩的猫。把这两个对象一起放到一个房间里,行为的意外组合便会出现。

本书与面向对象软件的设计有关,它将世界看作是对象之间一系列的自然交互。面向对象设计(OOD)要求你从认为世界是由一组预定义过程构成的观念,转变至认为世界是由多个对象之间的一系列消息传递构成。OOD的失败看起来很像是编码技术的失败,但它们实际上是视角的失败。学习如何进行面向对象设计的第一个要求便是要让自己沉浸在对象之中。一旦你采用面向对象的视角,那么其余部分也会水到渠成。

本书将通过沉浸式的过程给你指导。本章一开始会对OOD进行一般性的讨论。它首先是讨论设计的情况,接着会描述何时进行设计以及如何对它进行评判。在本章的结尾是对面向对象编程的概述,它定义了全书所用的术语。

1.1 设计赞歌
面向对象设计实践指南:Ruby语言描述
软件被建造出来总有它的原因。目标应用程序是全部的重点。无论这个程序是微不足道的游戏,还是要被用于指导辐射疗法,都一样。如果程序员们认为痛苦的编程工作就是以低成本方式生产工作软件,那么他们应该当机立断,要么忍气吞声地继续痛苦下去,要么另谋出路。

庆幸的是,你不必在快乐和生产力之间做出选择。实现快乐编码的编程技术与那些最高效地生产出软件的技术可以兼得。面向对象设计方法很好地解决了与编程相关的道德和技术两大难题。遵循这些方法能生产出低成本的软件,同时它所用的代码也一样让人乐于处理。

1.1.1 设计解决的问题
假设要编写一款新的应用程序。假设这款应用程序具备了一套完整、正确的需求。如果愿意,你也可以另外假设一件事情:一旦编写好了,这个应用程序绝不会再发生变化。

对于这种情况,设计就没那么重要了。像马戏团的表演者在一个没有摩擦力和重力的世界里旋转盘子一样,你可以将这个应用程序编成动画,然后自豪地退出来,并看着它一直不停地运转下去。无论多么不稳定,代码盘都会不断地旋转,虽然会有些摇晃,但绝不会掉下来。

只要什么都不改变,一切都会安好。

很不幸,事情总是会发生变化。这是永恒不变的。客户并不知道他们自己想要的是什么,他们没有说清自己的意图。你并不了解他们的需求,你已学会了如何把事情做得更好。即使那些在各方面都很完美的应用程序,也是不稳定的。该应用程序取得了巨大的成功,现在每个人都还想要得到更多。变化不可避免:它无处不在,无所不在,无法逃避。

变化的需求就是编程中的摩擦力和重力。它们会引入各种力,而这些力都会作用到周密的计划上,从而形成突发的和出人意料的压力。正是这种对变化的需求让设计变得如此重要。

易于更改的应用程序让人乐于去编写,也乐于去扩展。它们具有灵活性和适应性。那些抗拒变化的应用程序则正好相反:每一次的更改所付出的代价都很昂贵,每一次的更改都会让下一次的更改成本变得更高。几乎都没人愿意处理难以更改的应用程序。最严重的情况则会逐渐地变成了一部个人的恐怖电影:在里面你扮演的是一名倒霉的程序员,疯狂地从一个“转盘”跑到下一个,竭力想要逃离陶瓷摔碎的声音。

1.1.2 为何难以更改
面向对象的应用程序是由交互产生整体行为的各个零件组成。这些零件就是对象,而交互则体现在它们之间传递的那些消息里。想要获得发给正确目标对象的正确消息,需要这条消息的发送者对接收者有所了解。这一点会在这两者之间创建许多依赖关系,并且这些依赖关系还是处在不断变化之中。

面向对象设计与依赖关系管理(managing dependencies)相关。它是一套对依赖关系进行编排,以便各个对象能够容忍更改的编码技术。在缺乏设计的情形里,非托管的依赖关系很容易造成严重破坏,因为这些对象之间彼此了解太多。更改其中的任何一个对象便会强制要求其合作者也随之发生变化;与之相反,当其合作者发生变化时也一样会出现同样的情况……如此下去,永无止境。一个看似无关紧要的增强,会导致以同心圆交错方式向外辐射的破坏,最终无法更改任何代码。

当对象了解太多内容,它们对自己所在的世界便会有更多的期望。它们会变得很挑剔,它们会要求事情应该是“这个样子”。这些期望会对它们产生束缚。在不同的环境里,重用这些对象时,它们便会产生抗拒。它们测试起来是件痛苦的事,也难以被复制。

在一款小型应用程序里,糟糕的设计并无大碍。即使每一个对象与其他所有对象都连接在一起,只要在你的头脑里能够一次性全部掌控它们,那么你仍然可以改进这款应用程序。设计不当的小型应用程序所存在的问题是:如果它们成功了,那么它们将成长为设计不当的大型应用程序。它们会逐渐成为泥塘,那时你便不敢再涉足它;否则会沉得无影无踪。原本应该很简单的更改可能会引起应用程序的连锁反应,四处破坏代码,以及要求大量的重写。在这场交火中,测试也会被用上,然后它开始让人觉得更像是一种障碍,而非一种帮助。

1.1.3 实用的设计定义
每款应用程序都由代码集合而成,对代码的编排就是设计。对于两个独立的程序员,即使他们有着共同的设计构思,在解决同一问题时,也可能会采用不同的方式来编排代码。在流水线上同样训练有素的工人可以制造出相同的零件,但设计不是流水线。设计更像是一个工作室,在那里,志同道合的艺术家们雕刻出各具特色的应用程序。设计是一门艺术—一门编排代码的艺术。

设计之所以困难,部分原因在于每一个问题都涉及两个层面:你不仅要按今天的计划编写在将来要交付的代码,而且还必须要构建出能顾及到以后会发生变化的代码。在任何时间对过去所交付的测试版进行扩展,更改的成本最终都会侵蚀掉这个应用程序的最初成本。因为设计原则之间是交叉的,而且每一个问题都会涉及一个移动的时间表,所以设计挑战会有一大堆令人眼花缭乱的可行解决方案。你的工作具有很高的综合性:必须将对应用程序需求的总体理解,与各种设计选择的利与弊的知识组合在一起;然后设计出在当前算是低成本,而在将来也能继续保持那个样子的代码编排。

将未来情况考虑在内,似乎需要未卜先知的能力,这通常会被认为超出了编程领域。实际并非如此!设计所考虑的未来并不是指你可以预测未知的需求,并在当前从中选择一个来实现。程序员不是神仙。让设计预测未来的特定需求,几乎都不会有好的结果。实用的设计不会去预测未来将要发生什么,它只是接受什么事情会发生,并且也接受你现在所不知道的事情。不要对未来进行猜测,你对未来只保留有选择接受的权利。如果不做选择,则可以为以后的变化留出更大的余地。

设计的目的是允许你以后可以进行设计,而设计的首要目标是降低变化所带来的成本。

本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。

网友评论

登录后评论
0/500
评论
异步社区
+ 关注