如何遵守得墨忒耳法?

2022-09-01 19:42:15

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

可能有很多方法可以遵守这条定律(好的设计和规划是其中之一),但用非常简单的术语来说,这是一种遵守它的方法吗?

假设我有一个具有以下属性的类:

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

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

谢谢。


答案 1

正如Dancrumb上面所说,我认为法律的想法是确保人们在适当的水平上访问物体。

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

现在,假设我们有一个来自公关公司的旅游推广人,他想为他们的下一次巡演整理一些公关材料。我们可以用一个类来模拟他:

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

}

在现实生活中,这相当于旅游发起人打电话给办公室并说:

  • 旅游发起人:我可以和你的吉他手谈谈吗?

  • 接待员:好吧,我会帮你找他。

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

  • 旅游项目:嗨,我正在整理你最新的海报。只是想检查你的名字?

  • 吉他 手:这是吉米·佩奇

  • 旅游发起人:太好了,谢谢。哦,你能给我买你的鼓手吗?...

可以说,PA很快就会被解雇。大多数理性的人会问:“难道你不能为我们处理那个电话吗?

我们可以有一种方法,从技术上讲,这种方法会尊重德墨忒耳定律,但我们仍然要求我们的班级记住有关乐队的细节 - 即他们有一个吉他手 - 而这些信息应该真正属于乐队本身。getGuitaristsName()TourPromoter

为了确保我们以合理的方式引入方法,我们需要看看巡演发起人实际上在寻找什么 - 即所有乐队成员的名字。如果我们在代码中对该方法进行建模,它将为我们提供更大的灵活性,以便以后对方法进行更改,甚至不必触摸 :BandTourPromoter

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

如果我们现在添加一个贝斯手或键盘播放器,只有乐队类需要知道差异,而巡演发起人不需要改变。这意味着我们现在也遵守了单一责任原则 - 即我们的方法只需要在改变海报格式时改变,而不是在乐队改变时改变。makePosterText()

我不认为德墨忒耳定律会告诉你需要采用哪种方法才能以最佳方式满足原则(例如 而不是上面)这样,我认为你是对的 - 它确实向你展示了当事情坏了,但不一定如何解决它们。但是,牢记LoD意味着您可以留意可以通过其他设计原则(如SRP)的组合来修复的违规行为。getMembers()getGuitaristsName()


答案 2

您没有在适当的级别应用LoD:两者都应该被视为同一模块的一部分,并且您面临的困境应以最大的便利性为由决定。BandGuitarist

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

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