5

我有一个关于得墨忒耳定律与 Java 中其他对象中包含的列表有关的问题。我有以下课程。

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

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

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

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

经过一番阅读,这似乎违反了得墨忒耳定律,并在 Converstaion 中添加如下方法,这将是解决此问题的“更好”方法。

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

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

4

4 回答 4

8

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

啊,我记得当我刚开始的时候反复问自己这个问题——封装这个词是我为了测试而学到的,但并没有真正理解它的“必要性”。

我这么说是因为我们在这里谈论的是封装——对你的问题的一个词的回答:

conversationList通过getter访问——这就是您在此处发布的第二种模式的名称——将您的对话列表封装在对话类中。当您编写的程序具有三个或四个类的顶部并且具有既简单又固定的要求时,很难理解为什么这很重要:当您仍在思考这些小程序的工作原理时,整个概念“添加一个吸气剂”而不是直接访问字段似乎只是一件要做的事情,一件你并不真正理解为什么要这样做的事情,还有一件你可以搞砸的地方。

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

现在:假设你的老师改变了他明天给你的这个作业,给你一直在做的这个程序添加了一个无法预料的要求(顺便说一句,我认为这对老师来说是一件聪明的事情)。想象一下,您可以满足该要求的唯一方法是将对话列表保存在 aMap而不是 a 中List,这样您就可以将它们中的每一个映射到其他值。

如果您一直在使用“您的第一种方法” -conversationsList.add()在许多不同的类中直接调用以访问该字段,那么您将必须经历并更改每个类以调用conversations.put()而不是.add().

但是,如果您一直在使用第二种方法 - 通过 getter 访问您的集合 - 那么您可以更改该集合,无论您需要多么显着,您只需对该 getter 进行一次更改:

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

你的其他班级永远不会知道有什么改变,这很好,因为这意味着你编写的代码更少,特别是因为它让你不必做很多最糟糕的编码——所有的头发和咬牙切齿当您对其中一个类进行这些小更改之一时,通常会涉及捕获由于您所犯的小错误而产生的错误。

(另外,请参阅我对这个问题的回答以获得更好的说明)

于 2014-06-26T11:20:51.270 回答
0

使用第一种方法,您无法更改conversationList.

想象一下,您决定使用一个全新的类来处理消息,假设是MessagesManager,那么您必须更改每个认为是 . 的类中的每个调用List

但是有了public void addMessage(Message msg);,那么你可以使用

public void addMessageToList(Message msg)
{
    this.myMessagesManager.addMesasge(msg);
}

你已经封装了你的数据模型,Conversation用它做它想做的事Messages,没有人会关心。

于 2014-06-26T10:53:33.140 回答
0

这是角色和责任的问题。在第一种方法中,角色负责将消息添加到会话列表中,但实际上应该是会话对象的责任。在第二种方法中,您将该责任委托给 Conversation 对象。您告诉 Conversation 对象存储一条消息,仅此而已,您不在乎它是如何完成的。

于 2014-06-26T11:05:22.363 回答
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 用户(开发人员)将使用的上下文。它应该具有以下属性: - 自我表达 - 不足为奇 - 易于重构

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

于 2014-06-26T11:39:20.467 回答