得墨忒耳定律 - 为什么我需要使用吸气剂?

时间:2014-06-26 10:43:14

标签: java list law-of-demeter

我有一个关于Demeter法与Java中其他对象中包含的列表有关的问题。我有以下课程。

public class Conversation
{
    Person person;
    List<Message> conversationList;

    public List<Message> getConversationList()
    {
        return conversationList;
    }
}

要在此类的sessionList中添加新的Message对象,我通常会执行以下操作。

Conversationc = new Conversation();
c.getConversationList().add(new Message());

经过一段时间的阅读后,这似乎违反了得墨忒耳定律,并且如下所述在Converstaion中添加了一种方法,这将是“更好”的方法。

public List<Message> addMessageToList(Message msg)
{
    conversationList.add(msg);
}

然而,这对我来说似乎完全矫枉过正。在这种情况下,最佳做法是什么?

4 个答案:

答案 0 :(得分:9)

  

然而,这对我来说似乎完全矫枉过正。

啊,我记得在我刚开始的时候反复问自己这个问题 - 当封装这个词是我为测试学到的东西但是并没有真正理解&#34;必要性&#34;的。

我之所以这么说是因为封装就是我们在这里所说的 - 一个单词的答案:

通过getter访问conversationList - 这就是您在此处发布的第二种模式 - 封装会话类中的对话列表。当您编写的课程有三到四个课程并且要求既简单又固定时,很难理解为什么这很重要:当您仍然围绕着这些课程如何小程序工作,这整个概念&#34;添加一个吸气剂&#34;而不是直接访问字段似乎只是还有一件事要做,还有一件事,你不能真正理解为什么你必须做的事情,还有一个地方你可以搞砸了。

当然,这种感觉会随着你变得更加自信而消失,而其他会发生的事情是你会开始思考和研究更大更复杂的应用程序,并且需求会发生变化(无论是在现实中还是在对它们的感知中)随着时间的推移,这往往意味着你的程序需要改变 - 而且当封装将开始变得非常有意义时:

现在:假装你的老师改变了他明天给你的这项任务,为你正在研究的这个项目添加了一个不可预见的要求(顺便说一下,我认为这对于老师要做)。想象一下,您可以满足该要求的唯一方法是将对话列表保留在Map而不是List,这样您就可以将每个对话映射到其他值。

如果您一直在使用&#34;您的第一种方法&#34; - 调用conversationsList.add()直接在许多不同的类中访问该字段,然后您必须通过并更改其中的每个类来调用conversations.put()而不是.add()

但是,如果您一直在使用第二种方法 - 通过吸气剂访问您的收藏品 - 那么您可以根据需要更改该系列,并且您只需要进行一次更改即可:

 public void addMessageToList(Message msg)
{
    this.myMessagesManager.put(msg, new ArrayList<SomeValue>);
    //or .put(msg, null) or whatever
}

你的其他课程永远不会知道任何改变,这很好,因为这意味着你写的代码更少,特别很好,因为它让你不必做很多最糟糕的编码 - 所有的拔毛和咬牙切齿经常涉及捕捉你在做出这些小改动之一时所犯的小错误所引起的错误到其中一个班级。

(另请参阅my answer to this question以获得更好的说明)

答案 1 :(得分:0)

使用第一种方法,您无法更改conversationList的实现。

想象一下,你决定使用一个全新的班级来处理这些信息,让我们说MessagesManager,然后你必须改变每个认为它是{{1}的班级的每个电话。 }}

但是使用List,您可以使用

public void addMessage(Message msg);

您已封装了数据模型,public void addMessageToList(Message msg) { this.myMessagesManager.addMesasge(msg); } 使用其Conversation执行了所需的操作,而其他任何人都不会关心。

答案 2 :(得分:0)

这是角色和责任的问题。 在第一种方法中,Role负责向conversationList添加消息,但它实际上应该是Conversation对象的责任。 在第二种方法中,您将该职责委托给Conversation对象。 你告诉Conversation对象存储一个Message就是这样,你不关心它是如何完成的。

答案 3 :(得分:0)

两者都不是。方法的名称应表示将在对象实例上执行的操作。

在你的会话中,以add开头的方法有点含糊不清。所以问题是该消息的进一步行动是什么。

谈话有两个方面,你可以听或说。

converstion.listen(Message)conversation.listenMessage(Message)

当您设计API(公共方法)时,尝试使用它并查看它的行为。

conversation.listen(whatTomSad());
vs
conversation.listenMessage(whatTomSad());

在这个级别上很难决定。我会选择第一个。因为它提供了灵活性和适当的API抽象。

不要以为我们的API会增长,而且不仅仅是服务器Message

在解决方案的情况下,我们只重载该方法,并使API保持相当大的延伸,但我们不强迫用户采用。他可以轻松改变他的方法的返回类型。

conversation.listen(whatTomSad());
conversation.listen(whatTomSing())
vs
conversation.listenMessage(whatTomSad());
conversation.listenSong(whatTomSad();

简化和总结。方法名称不仅取决于对象,还取决于它的API(开发人员)用户将使用的上下文。它应该具有以下属性:   - 自我表达  - 不奇怪   - 易于重构

要平衡这是非常复杂的。最好的学习方法。观察其他开发人员的代码并从错误中学习。