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

时间:2013-06-30 17:49:05

标签: java oop solid-principles

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

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

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

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();
}

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

感谢。

4 个答案:

答案 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:BandGuitarist都应被视为同一模块的一部分,您应该以最大的便利为由决定您的困境。 / 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操作。