每当我看到关于得墨忒耳法的文章时,作者似乎都没有给出如何遵守这项法律的可靠实例。他们都解释了它是什么,并展示违法的例子,但这很容易。
可能有很多方法可以遵守这个法律(好的设计和计划就是一个)但是用非常简单的术语来说这是一种服从它的方法吗?
假设我有一个具有这些属性的类:
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();
}
这是你应该遵守法律的吗?
感谢。
答案 0 :(得分:42)
我认为,正如 Dancrumb 所述,法律的概念是确保人们在适当级别访问对象。
想象一下,我们的Band
班级为乐队办公室的前台建模。它的工作是充当乐队成员和PA的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类需要知道差异&旅行推广者不需要改变。这意味着我们现在也尊重单一责任原则 - 即如果我们更改我们的海报格式,我们的makePosterText()
方法只需要更改,而不是更改频段。
我不认为Demeter法会告诉你需要采用哪种方法以最佳方式达到原则(例如getMembers()
而不是getGuitaristsName()
以上)&通过这种方式,我认为你是对的 - 它确实会在事情被破坏时告诉你,但不一定能告诉你如何修复它们。考虑到LoD,意味着你要留意违规行为,然后通过结合其他设计原则(如SRP)来解决这些违规行为。
答案 1 :(得分:26)
您没有在适当级别应用LoD:Band
和Guitarist
都应被视为同一模块的一部分,您应该以最大的便利为由决定您的困境。 / p>
你的问题是一个更广泛问题的例子,我经常在关于设计模式和类似的书籍中遇到这些问题:他们试图解释广泛的原则,这些原则涉及复杂系统的设计,在上荒谬可笑小问题。结果只是读者的困惑。
你实际上看到这个原理实际上是这样的:你正在使用AsyncHttpClient
,这是一个在Netty
上构建的抽象,它是一个在Java NIO之上构建的抽象。现在,如果AsyncHttpClient
的API强迫您在某个地方直接管理Java NIO对象,其API更加原始,并处理完全对AsyncHttpClient
陌生的概念,那么这将是一个例子。破坏了。
答案 2 :(得分:4)
Demeter法本身并没有提出良好需求的方法。充其量,它是一种有用的启发式方法,用于识别可能存在问题的代码区域。偏离法律可被视为“code smell”
它可能会扩展类的接口,因为它会导致代理或包装器方法,使您可以访问内部对象拥有的属性。因此,它的优点应该与其缺点(可能创建笨重的类和模块)相平衡。
最好将其用作识别可能需要重构的代码的工具。但是,从
中简单地交换方法a.x.getFoo()
到
a.getXFoo()
遵循法律条文,没有遵循精神(对@TedHopp的承认)。相反,你应该研究用户在刺穿a
的抽象时试图做什么,以获得x
的{{1}}并相应地定义该方法。这可能不是一项微不足道的任务,但这就是为什么我们得到了巨额的报酬:)
答案 3 :(得分:2)
我有一个乐队回答谁在演奏这个乐器的问题?
public String whoIsPlaying(String instrument);
然后,如果你需要吉他手的名字,你会说:
band.whoIsPlaying("guitar");
也没有什么可以阻止你对其他乐器进行ping操作。