【问题标题】:OO Design EncapsulationOO设计封装
【发布时间】:2012-10-04 07:45:56
【问题描述】:

我有一个关于封装的问题。据我所知,封装可以使用私有/受保护的数据成员隐藏实现细节,并提供公共方法和属性来操作数据。这里的想法是防止类消费者直接修改数据成员。

但我担心返回私有/受保护数据成员的属性 getter 或其他公共方法。例如:如果我有这样的课程

public class Inventory
{
    private List<Guitar> guitars = new List<Guitar>();

    public void AddGuitar(string serialnumber, string price)
    {
        Guitar guitar = new Guitar(serialnumber, price);
        guitars.Add(guitar);
    }

    public List<Guitar> GetGuitars()
    {
        return guitars;
    } 
}

现在,如果 Inventory 类消费者调用 GetGuitars,他将获得在 Inventory 类中维护的吉他列表。现在消费者可以修改列表,例如删除/添加/修改项目。对我来说,看起来我们没有封装。我认为我应该在 GetGuitars() 中返回吉他列表项的副本。你怎么看?

我对封装的理解对吗?

谢谢

【问题讨论】:

  • 这或多或少是在决定是否严格遵守Law of Demeter 时面临的权衡。许多人会争辩说,正如您所做的那样,允许访问组合对象是务实的。像 C++ 这样的语言将允许 GetGuitars() 成为 const。
  • 防御性复制列表不会有太大帮助,只要吉他不是一成不变的或防御性复制的。遗憾的是,java 缺乏良好的不变性支持。
  • 伙计们,他问的不是Java。它是 C#(我猜)

标签: oop encapsulation


【解决方案1】:

通过使用合适的接口限制对它们的访问,可以很好地封装对象列表。

我认为您可以通过 AddGuitar 方法控制添加到列表中的内容是正确的,因为您可以控制其中的内容。恕我直言,您可以通过将 GetGuitar 更改为返回 IEnumerable 而不是 List 来加强此设计。

这减少了调用者对您的列表的控制,同时在返回抽象类型时也没有承诺。这样一来,您的内部数据结构也可以更改,而无需公共接口。

【讨论】:

    【解决方案2】:

    你是对的。使用像这样的设置器,客户可以修改列表。如果添加吉他需要一些特殊处理,则不需要这样做。在这种情况下,您有两种选择:

    1. 返回列表的副本(正如您已经建议的那样)。
    2. 用ReadOnlyCollection 将它包裹在getter 中。

    这两种情况都应记录在方法描述中,以便客户在尝试从外部修改列表时不会“感到惊讶”。

    【讨论】:

      【解决方案3】:

      如果你想让你的 List 数组不能被修改,为什么你不使用 AsReadOnly 方法:http://msdn.microsoft.com/en-us/library/e78dcd75.aspx

      关于成员内部的封装只能通过成员在外部不可用的方法进行读写。

      【讨论】:

        【解决方案4】:

        就风险而言,如果您返回一份使其不可修改的列表副本确实会更好(当您添加吉他时创建一个全新的不可修改列表,函数式编程风格)。

        在封装方面,最好去掉getGuitars() 方法,然后Inventory 类应该提供与之相关的功能(例如,printInventoryReport() 或其他)。这样,客户类就不需要知道您如何存储吉他,并将相关代码保存到 Inventory 类中。权衡是这个类变得更大,每次你需要吉他列表中的新东西时,你都需要修改Inventory。

        推荐一篇好文章:http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html 这在当时颇具煽动性,但我认为其中有很多真相。

        如果您继续使用吸气剂,一个小提示是选择您需要它是 List 还是 Collection 可以做到。甚至可能是Iterable!这样您就可以尽可能少地讲述您的实现,从而实现更好的封装。

        【讨论】:

          【解决方案5】:

          我同意返回列表会在封装方面留下一些不足之处。您可能需要考虑为单个项目编写一个 getter,或者可能是一个迭代器。该列表似乎是一个实现细节,因此其他类确实没有直接访问它的业务。

          【讨论】:

            【解决方案6】:

            这里有(至少)两个问题。

            首先是关于隐藏实现。您可以将“guitars”字段更改为数组或数据库,但您可以保留 AddGuitar 和 getGuitar 方法的签名不变,这样客户端代码就不会中断。

            第二个是关于您是否要返回吉他列表的防御性副本。获得吉他列表后,是否要添加和删除元素?既然你有添加吉他的方法,我假设没有。

            【讨论】:

              猜你喜欢
              • 2010-10-14
              • 1970-01-01
              • 1970-01-01
              • 2010-09-10
              • 1970-01-01
              • 2011-01-16
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多