报童、钱包和迪米特法则(设计模式迪米特原则经典论文翻译)

写在文章前:

  或许你写过无数代码,参与过很多大型系统的设计,但,你是否曾经思考过,你的设计可扩展、易维护么,在高速变化的互联网世界里,它能经得起这种急速变化的考验么?如果你没想过这些问题,那请先放下你那些牛逼的梦想,放下你的高傲,好好去理解、回味设计六大原则和23种设计模式,因为它们是你腾飞的基石。今天,我勇敢的尝试翻译一篇有关设计原则的经典论文,希望对大家有帮助。(翻译是一项很费时、费精力的活,而且博主英语水平也不是特别好,翻译时多采用意译,见谅)

前言

  在我读大学的时候,我的一个教授说每个程序员都有一个“装满各种小技巧的包”,这些小技巧,也就是解决问题的方法,在我们的个人经历中,会一遍又一遍的用到。他说,他的工作就是“把更多的小技巧装入我们的包里”。他所说的“小技巧”也就是如设计模式和设计风格(原文为idioms)。

  这篇论文要讨论的是一个我特别喜欢的“小技巧”。相对于被划为设计模式(经管常被划分为设计模式),把它划为设计风格可能更合适。在理解了这个设计风格并知道怎么使用它,我相信你的代码会更优雅、更不容易出错,且更易维护。我马上要讲述一个如何减少你代码中对象耦合的方法。这个方法有一个很棒的名字:迪米特法则

  

  现在我们假设有一个送报的小孩,他从他的顾客那里得到送报的钱。让我们为顾客定义一个类,几行代码表示送报小孩从顾客收钱,和一个送报小孩会执行的一个代码片段。

  

编写我们的代码

  好,让我们开始写顾客这个类。顾客类可能有个姓和名,可能有一个银行账户,或者购物地址等。为了例子的简单考虑,我们定义了一个简单的顾客类。

  

  考虑到倒霉的长度,我们省略了setters。从这个例子看,很显然我们还需要定义一个钱包类:

  

  好了,这是一个简单的钱包。以后可能给它添加各种数据结构,但这个例子,这个钱包足够了。

  现在利用这些,我们可以开始做一些有用的事了。让我们的送报小孩按响门铃,然后向顾客要求送报的报酬。如果我们把这写成代码,代码如下:

  

  这段代码表示送报小孩从顾客那拿到钱包,检查钱包以确保钱够,然后把钱从顾客钱包取出,放到自己钱包。

  

为什么这样很糟糕

  那么,这段代码糟糕在哪?好,让我们把这段代码用真实的生活场景还原。

  很显然,当送报小孩索要报酬时,顾客直接让小孩从自己的口袋取出钱包,并从钱包里拿走报酬。

  我不知道你会不会这样,反正我是很少让人拿走我的钱包。在现实中,上面的场景会遇到很多困难,更不用说相信那小孩很诚实,只拿走自己应得的报酬。如果以后钱包有信用卡啥的,他还能拿走信用卡。但是,这个问题的本质是,“小孩知道了更多他不需要知道的信息”

  那是一个很重要的概念。“送报小孩”这个类现在知道顾客有个钱包,而且还能操作它。当我们编译小孩这个类,我们还需要顾客和钱包这两个类。这三个类紧紧的耦合在了一起。如果我们改变钱包类,我们可能还需要修改其他两个类。

  还有一个经典的难题可能会遇到。如果顾客的钱包被偷了,那会发生什么?在我们这个例子中,或许昨晚有个贼就偷了这个钱包,然后有人把代码修改了,把钱包置为null,如下:

  victim.setWallet(null);

  这看起来像是一个很合理的假设。但是对于我们的送报小孩类呢?回头看看代码。我们假设钱包是存在的!我们的代码执行时将抛出空指针异常。

  我们可以在任何方法调用钱包类之前,检查它是否为空,但这会使我们的代码更乱。而真实场景会更坏--“如果我的顾客有钱包,就看看钱包里有多少钱……如果他能付钱,拿走钱”。很显然,我们被弄晕了。

修改原始代码

  解决这个问题的更合适的方式是采用符合现实场景的方法:“当小孩来到门前要求报酬时,顾客不会把钱包交给小孩,而小孩甚至都不需要知道顾客有个钱包”。

  

  注意,顾客不再有"getWallet()"方法了,但是他有一个getPayMent方法。让我们看看小孩的:

  

为什么现在更好

  首先,现在的模型更好的符合现实世界的情况。小孩现在是向顾客索要报酬,而没有机会拿到钱包。

  其次,钱包现在可以改变了,但小孩和这种变化彻底隔离开来了。如果钱包的被偷了,顾客会换个钱包,但只要顾客提供的接口保持不变,顾客的客户端不会关心顾客是否换了一个新钱包。代码变得更易维护了。

  第三,可能也是最面向对象的理由,我们可以随意改变‘getPayment()‘的实现了。在第一个例子,我们假设顾客有一个钱包,这导致了我们讨论的空指针异常。在现实世界里,当小孩来到门前,我们的顾客可能从抽屉里取出钱,或者从室友那借。但所有这些业务逻辑,小孩都不需要关心了。所有这些都在‘getPayment()‘中实现,并且在将来可以改变而不影响小孩的代码。

  

你也可以听起来更智慧

  在开头我提到过这关概念有个名字,叫做“迪米特法则”

  一个方法的对象应该只引用该方法中的四种对象(注:可以简单称为"只与直接朋友通信"):

  1、它自己

  2、它的方法参数

  3、它创造或者实例化的任何对象

  4、有直接关联的对象

  

什么时候、怎么使用迪米特法则

  1、get的链

  最常用到的地方就是,如下

  value = object.getX().getY().getTheValue();

  (注:通过不停的get获得不同的对象,这样就与很多非直接朋友通信了,所以可以简单记为“减少点”,不过并不是“点多就是违反法则”。不过有篇文章介绍了,请参考The Law of Demeter Is Not A Dot Counting Exercise

)

  2、很多临时对象

结论

  我希望这篇论文能带给你一些新的东西。或许这些几乎就像常识一样普通。如果真的是这样,那很好。但是,理解它,给它一个统一的命名,然后人们可以使用它相互交流也是很好的。

原文请参考

  http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter.pdf

时间: 2024-10-11 03:48:34

报童、钱包和迪米特法则(设计模式迪米特原则经典论文翻译)的相关文章

设计模式——迪米特法则(最少知识原则)

迪米特法则: 如果两个类不必彼此直接通信,那么这两个类就不要发生直接的相互作用. 如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用. 迪米特法则首先强调的前提是:在类的结构设计上,每一个类都应该尽量降低成员的访问权限,也就是说,一个类包装好 自己的private状态,不需要让别的类知道的字段或行为就不要公开. 迪米特法则的根本思想是:强调了类之间的松耦合. 在程序设计的时候,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及. 也就是说信

学习日记之迪米特法则、外观模式和 Effective C++

迪米特法则(最少知识原则):如果两个类不必彼此直接通信,那么两个类就不应该发生直接的相互作用.如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用. (1),在类的结构设计上,每一个类都应当尽量降低成员的访问权限. (2),迪米特法则的根本思想是强调了类的松耦合. (3),类之间的耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成影响. 外观模式(Facade):为了系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加

面向对象设计原则之迪米特法则

迪米特法则来自于1987年美国东北大学(Northeastern University)一个名为“Demeter”的研究项目.迪米特法则又称为最少知识原则(LeastKnowledge Principle, LKP),其定义如下: 迪米特法则(Law of  Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用. 如果一个系统符合迪米特法则,那么当其中某一个模块发生修改时,就会尽量少地影响其他模块,扩展会相对容易,这是对软件实体之间通信的限制,迪米特法则要求限制软件实体之

设计模式之设计原则学习

设计模式的设计原则包含了:单一职责原则.里氏替换原则.依赖倒置原则.接口隔离原则.迪米特法则和开闭原则等6大原则. 单一职责原则(Single Responsibility Principle,简称SRP),英文介绍为:There should never be more than one reason for a class to change,即一个类,应当只有一个引起它变化的原因.单一职责原则,要求对象不能承担太多的职责,充分保证对象的高内聚.单一职责的优点有:1.降低了类的复杂性:2.提

设计模式之六大原则(转载)

关于设计模式的六大设计原则的资料网上很多,但是很多地方解释地都太过于笼统化,我也找了很多资料来看,发现CSDN上有几篇关于设计模式的六大原则讲述的比较通俗易懂,因此转载过来. 原作者博客链接:http://blog.csdn.net/LoveLion/article/category/738450/7 一.单一职责原则 原文链接:http://blog.csdn.net/lovelion/article/details/7536542 单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大

设计模式概述与原则

一 . 设计模式概述 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型 模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式. 组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任 链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 具体如下: 其中创建型有: 一.Singleton,单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问

【设计模式的六大原则】

设计模式的六大原则 1.开闭原则(Open Close Principle) 开闭原则就是说对扩展开放,对修改关闭.在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果.所以一句话概括就是:为了使程序的扩展性好,易于维护和升级.想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点. 2.里氏代换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的

从设计模式的设计原则感悟生活

设计模式中的很多思想还是很有意思的,刚毕业的时候接触设计模式感觉有点高深,坐而论道,感觉还是有些虚,平时做的小练习还能自己捣鼓一番,自己使用一下设计模式,然后大刀阔斧的改动代码,随心所欲,写完以后还能热乎劲上来高兴几天,时间长了就忘了很多的东西,只是感觉设计模式就是抽象,自己感觉也抽象.如果死磕着问自己,估计大脑里还是没什么印象了.到了工作中,又发现有些功能或者思想固然好,但是要在已有的项目中做一些改动,一下子又有点不知所措,时间长了,思想和行动就脱节了.工作中以面向对象为目标,实际中做着结构化

PHP面向对象设计模式和设计原则

一.设计模式 1.单例模式 目的:让一个类只能生成一个对象 步骤:    ①将构造函数变成私有的 ②在类里面做一个静态成员函数造对象 ③在方法里加限制条件 //单例模式 class Ren{ //定义人类 public static $r; //设定静态成员变量,为了可以带入静态成员函数中 public $name; //普通成员变量,用于测试是否只实例化了一个对象 private function __construct(){ //私有化构造函数,阻止实例化对象 } public static