日历

September 2020
M T W T F S S
 123456
78910111213
14151617181920
21222324252627
282930  

迪米特法则

Home Forums 《冒号课堂》讨论区 迪米特法则

  • This topic is empty.
Viewing 12 reply threads
  • Author
    Posts
    • #1085
      Todd
      Member

      目前,我能够识别并避免违背迪米特法则,但是总感觉对迪米特法则的认识深度还不够,还不能说清楚到底迪米特法则本质上意味着什么。我目前思考的结果是:

      OOP中,对象间的交互是消息传递模型,而消息是值语义的;而obj.GetAnotherObject()的结果是引用,违背了对象消息传递模型。所以,违背迪米特法则的设计其实是不符合OOP的。

    • #1300
      hui
      Keymaster

      迪米特法则(以下简称LoD)提倡一个类尽量不要与其间接关联的类发生直接对话。这一方面有助于减少类之间的关联,从而降低系统的耦合度;另一方面有助于界定职责范围,从而增强了系统的内聚度。从消息传递的角度看,LoD可看作一种对象交流原则:只吩咐不询问(tell, don’t ask)(可参见 http://www.pragprog.com/articles/tell-dont-askhttp://www.c2.com/cgi/wiki?TellDontAsk )。当然,LoD最终目的是为了降低软件的脆弱性、增强软件的可维护性。

      LoD虽然不是必须遵守的铁律,但一个有经验的程序员在违背它时,会听到身后的警告声:你知道得太多了:)

    • #1301
      Todd
      Member

      谢谢,推荐的文章很好,看了之后我对LoD的认识加深了不少!

      另外,还有一个意外的收获,即文中提到了CQRS。以前我觉得很多post condition/class invariant不容易在实际编程中表达,一个重要原因是assert语句不应该有副作用,现在我知道如果类的设计能采用CQRS的方式,那么就很容易写出无副作用的assert语句来表达post condition/class invariant。

      另外,最近关于CQRS还看到一些持久化方面的应用,普通的持久化方案是基于对象内部状态的,而采用CQRS,把所有的commands持久化下来就得到了对象的状态以及状态历史。

    • #1302
      Todd
      Member

      >>LoD虽然不是必须遵守的铁律,但一个有经验的程序员在违背它时,会听到身后的警告声:你知道得太多了:)

      http://www.aspiringcraftsman.com/2008/06/distilling-law-of-demeter

      这篇文章讲了LoD的思想也提到了LoD不是任何场合都适用。我想到一个例子:Company与CEO的关系,CEO ceo = company.GetCEO()是违反LoD的,但这又是领域模型所需要的。

      我曾经想能不能把软件设计得像汽车一样,具有清晰地功能层次和子系统边界。后来发现现实的领域模型比机械结构要复杂得多,更接近于错综复杂的网状,而不是机械的树状结构。这种追求是一种理想的情况,但不是总能达到。

    • #1303
      hui
      Keymaster

      没错,现实中的模型经常是网状结构的,如何尽可能地减少非必要关联、横向或纵向分割复杂关联正是软件设计的最大挑战之一。

    • #1304
      Lumj
      Member

      我很难理解tell,don’t ask这个原则

      我看了在2L给出的文章,

      That is, you should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do

      使外界能了解自己的状态不正是对象的职责吗?不知这个理解哪里有误?

      不过我猜想是否这样:

      首先,ask没问题,可是不该以ask的结果为依据对其tell

      然后,”ask,decide,then tell”针对的是对象职责范围内的逻辑

      我想到个例子:

      if(someControl.isVisible())

      someControl.hide();

      就是对象的职责外泄,因为”在已经隐藏的情况下无须调用hide”是Control自然该管的,正确的做法是:

      someControl.hideIfVisible();

      而同样是”ask,decide,then tell”,却不可能将

      if(someNumber.isLargerThan(100))

      someNumber=100;

      敛为Number.truncateIfLargerThanOneHundred供人调用,因为100这个数字在只谈论Number的时候是个magic number,它的语义是在Number以外才得以实现的

    • #1305
      hui
      Keymaster

      用Number作例子是不恰当的,如果这个Number是基本类型的话。相反,如果Number包含足够的业务逻辑,那么magic number的处理正应该是Number的职责。

      一个通俗的tell, don’t ask的例子是:要一只狗跑,不是dog.getLegs().run(),而是dog.run(),即不是指挥狗的腿来跑,而是由狗自己负责调用腿来跑。事实上,指挥者根本不在乎狗有没有腿,只在乎它能不能跑。至于它用腿跑,还是聪明到能用嘴控制某个机动车来跑,则是狗的职责,不是指挥者的职责。因此“tell, don’t ask”是一种良好的职责分工原则,能使代码保持合理的抽象层次划分。

      可以这么理解:tell代表一个抽象的命令,而ask代表(被命令对象的)具体执行方法或步骤。

    • #1306
      Lumj
      Member

      “tell代表一个抽象的命令,而ask代表(被命令对象的)具体执行方法或步骤”

      嗯,这样的话便容易理解了,我知道了

      我本来的理解是一种荒唐的理解:

      if(nameTextBox.getText()=="Lumj"){//ask,decide

      alert("This is our admin's name,you cannot use it");

      nameTextBox.setText(String.empty);//tell

      }

    • #1307
      Lumj
      Member

      最近对这个法则又有了更多的体会

      LoD是PIE(Program intensively and expressively)原则的一种很直接的体现

      intensively,and expressively,其实即是不要降级语义

      要做什么事,就让对象对这种情况知情和付诸关注,而不要自己在对象外实现掉一层(更有甚者是若干层)高层语义,再把剩下的转发给对象

      隐晦和拐弯抹角即意味着不受支持,不会在发生变化时被对象纳入考虑,从而脱节

      象dog.legs.run()的情况即是:这条狗并不懂得run,是外界隐晦地告诉它的,”dog会run”这层语义即是名存实亡的,是Dog外部的一厢情愿

    • #1308
      hui
      Keymaster

      PIE原则是“program intently and expressively”,intently与intensively不是一回事。你的说法虽然有些模糊,但还是抓住了一个要点,即:LoD涉及抽象层次问题(语义与抽象密切相关),一个对象尽量不要与不在同一抽象层次的另一对象打交道。比如,此处人(作为狗的命令者)与狗处于同一层次,但狗的腿则处于更下一层。人应当与狗打交道,而不是它的腿。

    • #1309
      Lumj
      Member

      呃..原来我还搞错了一个词..

      每次都是扫到这个词的,我迅雷不及掩耳地就以为那个词是intend变来的一个形容词,觉得这个词相当传神呢..

    • #1310
      Todd
      Member

      >>一个对象尽量不要与不在同一抽象层次的另一对象打交道。

      我认为C和C++的指针之所以危险,一个重要原因就是它同时具备了两个抽象层次,一是底层的内存地址;一是高层的对象引用。

    • #1311
      Lumj
      Member

      对的

Viewing 12 reply threads
  • You must be logged in to reply to this topic.
 请您评分1星(很差)2星(不行)3星(一般)4星(不错)5星(很棒)