5

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

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

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() 中返回吉他列表项的副本。你怎么看?。

我对封装的理解正确吗?

谢谢

4

6 回答 6

1

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

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

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

于 2012-10-04T08:14:33.247 回答
1

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

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

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

于 2012-10-04T08:18:26.407 回答
1

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

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

于 2012-10-04T08:20:43.917 回答
1

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

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

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

如果你和吸气剂呆在一起,一个小提示是选择你需要它是 aList还是Collectioncan do。甚至可能是一个Iterable!通过这种方式,您可以尽可能少地讲述您的实现,从而实现更好的封装。

于 2012-10-04T08:21:39.797 回答
0

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

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

第二个是关于您是否要返回吉他列表的防御性副本。获得吉他列表后,是否要添加和删除元素?由于您有添加吉他的方法,因此我认为没有。

于 2012-10-04T08:10:55.123 回答
0

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

于 2012-10-04T07:58:14.640 回答