如何遵守得墨忒耳的法律?

每当我看到关于得墨忒耳法的文章时,作者似乎都没有给出如何遵守这一法律的可靠实例。 他们都解释了它是什么,并展示违法的例子,但这很容易。

可能有很多方法可以遵守这个法律(好的设计和计划就是一个),但用非常简单的术语来说,这是一种服从它的方法吗?

假设我有一个具有这些属性的类:

public class Band { private Singer singer; private Drummer drummer; private Guitarist guitarist; } 

我在程序中的某个地方,我有一个Band类的实例,我想要吉他手的名字,我通常看到的是:

 guitaristName = band.getGuitarist().getName(); 

那个似乎并不太糟糕,因为链条不会太深,但是得墨忒耳法则说它可能应该这样做:

 guitaristName = band.getGuitaristName(); 

我的Band类有一个方法:

 public String getGuitaristName() { return guitarist.getName(); } 

这是你应该遵守法律的吗?

谢谢。

你没有在适当的级别申请LoD: BandGuitarist都应被视为同一模块的一部分,你所面临的困境将以最大的便利为由决定。

你的问题是一个更广泛的问题的例子,我经常在关于设计模式和类似的书籍中遇到这些问题:他们试图解释涉及复杂系统设计的广泛原则,关于可笑的小问题。 结果只是读者的困惑。

你实际上看到这个原理实际上是这样的:你正在使用AsyncHttpClient ,这是一个在Netty构建的抽象,它是一个在Java NIO之上构建的抽象。 现在,如果AsyncHttpClient的API强迫你在某个地方直接管理一个Java NIO对象,其API更加原始,并且处理完全与AsyncHttpClient概念,那就是破坏LoD的一个例子。

我认为,正如Dancrumb所说,法律的概念是确保人们在适当的级别访问对象。

想象一下,我们的Band类为乐队办公室的前台建模。 它的工作是充当乐队成员的PA,并与想要与乐队互动的任何人打交道。

现在让我们说我们有一个来自公关公司的旅游推广人,他们希望为他们的下一次旅行整理一些公关材料。 我们可以用一个类对他进行建模:

 class TourPromoter { public String makePosterText(Band band) { String guitaristsName = band.getGuitarist().getName(); String drummersName = band.getDrummer().getName(); String singersName = band.getSinger().getName(); StringBuilder posterText = new StringBuilder(); posterText.append(band.getName() posterText.append(" featuring: "); posterText.append(guitaristsName); posterText.append(", "); posterText.append(singersName); posterText.append(", ") posterText.append(drummersName); posterText.append(", ") posterText.append("Tickets £50."); return posterText.toString(); } } 

在现实生活中,这相当于旅游推广人员在办公室打电话说:

  • 旅行推广者:我可以和你的吉他手说话吗?

  • 接待员:好的,我会帮他的。

  • 吉他手:您好,这是吉他手

  • Tour Promter:嗨,我正在整理你的最新海报。 只是想检查一下你的名字?

  • 吉他手:这是Jimmy Page

  • 旅行推广:很好,谢谢。 哦,你能把我当作鼓手吗?……

我只想说PA会很快被解雇。 最合理的人会问:“你不能为我们处理这个电话吗?”

我们可以有一个getGuitaristsName()方法,它在技术上会遵守得墨忒耳法则,但我们仍然要求我们的TourPromoter类记住有关乐队的细节 – 即他们有一个吉他手 – 而这些信息应该属于乐队本身。

为了确保我们以合理的方式介绍方法,我们需要查看旅行推广者实际寻找的内容 – 即所有乐队成员的名字。 如果我们在代码中对该方法进行建模,它可以让我们更灵活地在以后更改Band ,而不必触及TourPromoter

 public class Band { private Singer singer; private Drummer drummer; private Guitarist guitarist; public String[] getMembers() { return {singer.getName(), drummer.getName(), guitarist.getName()}; } } public class TourPromoter { public String makePosterText(Band band) { StringBuilder posterText = new StringBuilder(); posterText.append(band.getName()); posterText.append(" featuring: "); for(String member: band.getMembers()) { posterText.append(member); posterText.append(", "); } posterText.append("Tickets: £50"); return posterText.toString(); } } 

如果我们现在添加一个Bassist或KeyboardPlayer,只有Band类需要知道差异并且Tour Promoter不需要更改。 这意味着我们现在也同样遵守单一责任原则 – 即我们的makePosterText()方法只需要改变我们的海报格式,而不是在频段改变时。

我不认为Demeter法会告诉你需要采用哪种方法以最佳方式满足原则(例如getMembers()而不是上面的getGuitaristsName() )并且以这种方式,我认为你是对 – 它会在事情被破坏时向您显示,但不一定如何解决它们。 考虑到LoD,意味着你要留意违规行为,然后通过结合其他设计原则(如SRP)来解决这些违规行为。

Demeter法本身并没有提出良好需求的方法。 充其量,它是一种有用的启发式方法,用于识别可能存在问题的代码区域。 偏离法律可被视为“ 代码味 ”

它可能会扩展类的接口,因为它会导致代理或包装器方法,使您可以访问内部对象拥有的属性。 因此,它的优点应该与其缺点(可能创建笨重的类和模块)相平衡。

它最适合用作识别可能需要重构的代码的工具。 但是,从中简单地交换方法

 axgetFoo() 

 a.getXFoo() 

遵循法律条文,没有遵循精神(对@TedHopp的承认)。 相反,你应该研究用户在为了获得xfoo并相应地定义该方法时为了获取xfoo而试图做什么。 这可能不是一项微不足道的任务,但这就是为什么我们得到了大笔收入:)

我有一个乐队回答谁在演奏这个乐器的问题?

 public String whoIsPlaying(String instrument); 

如果你需要一个吉他手的名字,你会说:

 band.whoIsPlaying("guitar"); 

没有什么可以阻止你ping其他乐器。