Oop 面向对象设计封装

Oop 面向对象设计封装,oop,encapsulation,Oop,Encapsulation,我有一个关于封装的问题。据我所知,封装允许使用私有/受保护的数据成员隐藏实现细节,并提供对数据进行操作的公共方法和属性。这里的想法是防止类使用者直接修改数据成员 但我关心返回私有/受保护数据成员的属性getter或其他公共方法。例如:如果我有这样的课 public class Inventory { private List<Guitar> guitars = new List<Guitar>(); public void AddGuitar(strin

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

但我关心返回私有/受保护数据成员的属性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;
    } 
}
公共类目录
{
私有列表吉他=新列表();
公共无效添加吉他(字符串序列号、字符串价格)
{
吉他=新吉他(序列号,价格);
吉他。添加(吉他);
}
公共列表GetGuitars()
{
返回吉他;
} 
}
现在,如果Inventory类消费者调用GetGuitars,他将获得Inventory类中维护的吉他列表。现在消费者可以修改列表,如删除/添加/修改项目。对我来说,看起来我们没有封装。我想我应该返回GetGuitars()中吉他列表项的副本。你觉得怎么样

我对封装的理解正确吗


谢谢

我同意返回列表在封装方面还有一些需要改进的地方。您可能想考虑为单个项编写一个吸气剂,或者可能是迭代器。这个列表看起来像是一个实现细节,所以其他类确实没有必要直接访问它。

这里(至少)有两个问题

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


第二个问题是你是否想要返回吉他列表的防御性副本。一旦你有吉他列表,你想添加和删除元素吗?由于您有一种添加吉他的方法,我想不会。通过使用合适的接口限制对对象的访问,可以很好地封装对象列表

我认为你通过AddGuitar方法控制列表中的添加是正确的,因为你可以控制列表中的内容。IMHO,您可以通过修改GetGuitars以返回IEnumerable而不是List来加强这种设计


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

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

  • 返回列表的副本(如您所建议的)
  • 把它包在吸气剂里
    这两种情况都应记录在方法说明中,以便客户端在尝试从外部修改列表时不会感到“意外”。

    如果您希望列表数组无法修改,为什么不使用AsReadOnly方法:


    关于封装内部成员只能通过无法从外部获得成员的方法写入和读取。

    就风险而言,最好返回一份“使其不可修改”列表的副本(添加吉他时创建一个全新的不可修改列表,函数式编程风格)

    就封装而言,最好去掉
    getGuitars()
    方法,然后
    Inventory
    类应该提供与其相关的功能(例如,
    printInventoryReport()
    或其他)。这样,客户机类根本不需要知道如何存储吉他,如何将相关代码保存到
    库存
    类中。权衡的结果是,这个类变得越来越大,每次你需要从吉他列表中获得新的东西时,你都需要修改
    目录

    我推荐一篇好文章: 这在当时是相当煽动性的,但我认为这里面有很多事实


    如果您继续使用getter,一个小提示是,如果需要它是
    List
    Collection
    可以选择。甚至可能是一个
    Iterable
    !通过这种方式,您可以尽可能少地讲述您的实现,从而实现更好的封装。

    这或多或少是决定是否严格遵守应用程序时所面临的权衡。许多人会争辩说,像您所做的那样,允许访问组合对象是务实的。像C++这样的语言将允许<>代码> GeGeTalgSARS()/<代码> >代码> const 。遗憾的是,java缺乏良好的不变性支持。是C#(我猜)