我对封装有疑问。据我所知,封装允许使用私有/受保护数据成员隐藏实现细节,并提供操作数据的公共方法和属性。这里的想法是防止类消费者直接修改数据成员。
但我对属性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()中返回吉他列表项的副本。你怎么看?。
我对封装的理解是对吗?
由于
答案 0 :(得分:1)
通过使用合适的接口限制对对象的访问,可以很好地实现封装对象列表。
我认为您可以通过AddGuitar方法控制对列表的添加,因为您可以控制进入的内容。您可以通过更改GetGuitars来返回IEnumerable而不是List来强化此设计,恕我直言。
这会减少调用者对列表的控制,同时也会在返回抽象类型时失去控制权。这样,您的内部数据结构可以在不需要公共接口的情况下进行更改。
答案 1 :(得分:1)
你是对的。使用类似的setter,客户端可以修改列表。如果添加吉他需要一些特殊处理,这是不希望的。在这种情况下,您有两种选择:
这两种情况都应记录在方法描述中,以便客户在尝试从外部修改列表时不会感到“惊讶”。
答案 2 :(得分:1)
如果你想要你的List数组无法修改,为什么你不使用AsReadOnly方法:http://msdn.microsoft.com/en-us/library/e78dcd75.aspx
关于成员内部封装的只能通过外部无法获得成员的方法进行写入和读取。
答案 3 :(得分:1)
就风险而言,如果你返回一份无法修改的列表副本确实更好(当你添加吉他时,创建一个全新的不可修改的列表,函数编程风格)。
就封装而言,最好摆脱getGuitars()
方法然后Inventory
类应提供与之关联的功能(例如,printInventoryReport()
或其他) 。这样,任何客户端类都不需要知道如何存储吉他,并将相关代码保存到Inventory
类中。权衡是这个类变得更大,每当你需要吉他列表中的新东西时,你需要修改Inventory
。
我推荐一篇好文章:http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html 那天它非常煽动,但我认为那里有很多真相。
如果你坚持使用吸气剂,那么一个小小的提示就是选择你是List
还是Collection
能做到的。甚至可能是Iterable
!通过这种方式,您可以尽可能少地了解实现,从而实现更好的封装。
答案 4 :(得分:0)
我同意返回列表会在封装方面留下一些需要的东西。您可能需要考虑为单个项目编写getter,或者可能考虑为迭代器编写getter。该列表看起来像一个实现细节,因此其他类实际上没有业务直接访问它。
答案 5 :(得分:0)
这里至少有两个问题。
第一个是关于隐藏实现。你可以改变吉他&#34;字段到数组或数据库但你可以保留方法AddGuitar和getGuitars的签名不变,所以客户端代码不会中断。
第二个是关于你是否想要返回吉他列表的防御性副本。一旦你有吉他列表,你想添加和删除元素吗?既然你有一种添加吉他的方法,我认为不会。