封装 - 当设置者已经公开时我们为什么需要它?

时间:2015-09-16 08:26:41

标签: java c++ .net oop encapsulation

封装正在隐藏数据。我想在这里听到一些非常有趣的答案。

当我们已经为变量声明private setter方法时,将变量保持为public的原因是什么?

我理解封装的用法,但是当我们将setter设为公开时,将变量保持为private的背后是什么,我们可以直接使用public访问修饰符

是不是因为我们不希望其他人知道我们在后端存储数据或管理数据的确切方式?

9 个答案:

答案 0 :(得分:31)

  

是不是因为我们不希望别人知道我们的确切方式   在后端存储数据或管理数据?

是的,这就是重点。它也与抽象信息隐藏的概念有关。

您提供的公共setter在由类客户端调用时将具有您已记录的效果。客户的业务如何实现这种效果并非如此。你在修改其中一个类属性吗?好吧,让客户知道,但不是你实际修改变量的事实。在将来,您可能希望修改您的类,以便它使用完全不同的东西(属性字典?外部服务?无论什么!)而不是简单的备份变量,而客户端不会破坏。

因此,您的setter是抽象,您提供给客户端"修改此类属性"。与此同时,你隐藏你正在使用内部变量这一事实,因为客户并不需要知道这一事实。

(注意:这里我使用"属性"作为一般概念,与任何具体的编程语言无关)

答案 1 :(得分:23)

我完全赞同Konamiman's answer,但我想补充一点:

有些情况下你真的不想要那种抽象。那没关系。

我喜欢在这里使用的一个简单示例是一个三维浮点向量的类:

class Vector3f {
public:
    float x;
    float y;
    float z;
};

您可以将这些字段设为私有并提供setter吗?当然,你可以。但是在这里你可能会争辩说,这个类实际上只是提供了一个浮点元组,你不需要任何额外的功能。因此,添加setter只会使课程复杂化,而你宁愿将这些字段公开。

现在,您可以轻松构建可能会在以后咬你的场景。例如,您可能有一天会要求Vector3f不允许存储NaN,并且如果有人试图这样做,则应该抛出异常。但是这样一个假设的未来问题不足以证明引入额外的抽象是合理的。

作为程序员你的调用,决定哪些抽象对手头的问题有意义,哪些抽象只会妨碍你完成工作。不必要的抽象是over-engineering,并且会损害你的生产力,就像不抽象一样。

结论:不要因为某人声称这是一个好习惯而盲目地使用setter。相反,考虑手头的问题并考虑权衡。

答案 2 :(得分:11)

因为通过封装,我们提供单点访问。假设你定义一个变量及其setter如下

String username; 

public void setUsername(String username){
this.username = username;
}

稍后您想在设置username属性之前添加一些验证。如果您通过直接访问该属性将用户名设置为10个位置,那么您没有单点访问权限,您需要在10个位置进行此更改。但是,如果您有一个setter方法,那么通过在一个地方进行更改,您可以轻松实现结果。

答案 3 :(得分:5)

虽然Konamiman answer已成立,但我想补充一点,在公共制定者的特定情况下,直接暴露您要求的公共字段,除了信息隐藏解耦实现与公共表面或 API 之外,还要记住另一个非常重要的区别;的 验证

在公共字段方案中,无法在修改字段值时对其进行验证。如果是公共setter(无论是Foo {get; set;}属性还是SetFoo(Foo value))方法,您可以添加验证代码并启动所需的副作用,这样可以确保您的班级始终处于有效或可预测的状态。

答案 4 :(得分:4)

想想这个:我代表一个真实的生活对象,一个狮子通过一个班级。我做这样的事情。

class Lion {
    public int legs;
} 

现在我的类需要其他开发人员来创建一个对象并设置它的leg字段。他做了这样的事情

Lion jungleKing = new Lion();
jungleKing.legs=15;

现在的问题是,Java不会限制他设置任何超过4的数字作为该对象的腿数。这不是一个错误,而且运行得很好。但这是一个逻辑错误,编译器不会帮助你。这样狮子可以有任意数量的腿。 但是,如果我们以这种方式编写代码

class Lion {
    private int legs;
    public void setLegs(int legs){
        if(legs>4)
            this.legs=4;
        else this.legs=legs;
    } 
} 

现在你没有任何超过4条腿的狮子会因为更新课程领域的政策已经由班级本身定义,并且没有人不知道政策的进展更新leg字段是因为更新leg字段的唯一方法是通过setLegs()方法,该方法知道类的策略。

答案 5 :(得分:3)

如果您想在分配前进行范围检查,该怎么办?这是我使用setter和getter的案例之一

答案 6 :(得分:2)

我在实践中遇到的或多或少简单实际的例子是Options类,它有很多setter和getter。在某些时候,您可能希望添加取决于其他选项或具有副作用的新选项。甚至用Enum替换选项组。在这种情况下,setA函数不仅会修改a字段,还会隐藏一些额外的配置逻辑。同样,getA不仅会返回a的值,还会返回config == cStuffSupportingA之类的内容。

答案 7 :(得分:2)

Wikipedia对[mutator方法(https://en.wikipedia.org/wiki/Mutator_method)有一个很好的概述,这就是setter方法以及它们如何在不同语言中工作。

简短版本:如果你想引入验证或其他在对象修改时执行的逻辑,那么让一个setter放入该逻辑是很好的。另外你可能想隐藏你存储的东西。因此,这些是获得/获得者的原因。同样,对于getter,您可能具有提供默认值或值的逻辑,这些默认值或值依赖于例如。 Locale,字符编码等等的配置。除了获取或设置实例变量之外,还有许多合理的理由想要获得逻辑。

显然,如果你有getter和setteres,你不希望人们通过直接操作对象状态来绕过它们,这就是你应该保持实例变量私有的原因。

要考虑的其他事项包括你是否真的希望你的对象完全可变(如果没有,make field final),是否要修改对象状态线程安全,例如:锁,同步等

答案 8 :(得分:2)

将字段设置为私有文档是一个强大的事实:这些私有字段仅直接在当前类中使用。这有助于维护人员不必追踪现场使用情况。他们可以通过查看类并通过类'确定这些字段的影响来更好地对代码进行推理。环境通过公共和受保护的方法调用。它限制了课堂上的曝光表面。

反过来,定义一个&#34; setter&#34;私人领域并不是要再次宣传。它是关于声明另一个强大的事实:属于该类的对象具有可以从外部修改的属性。 (术语对象属性用于整个的有界部分,关于此部分的可观察事实< / em>,不在OOP意义上)

为什么然后宣布一个&#34; setter&#34;在场上公开场地就足够了?因为声明字段不仅会将名称绑定到类的对象属性,还会承诺为此属性使用内存存储

因此,如果您使用setter&#34;声明一个&#34;私有字段,则声明三件事:

  • 您声明您为字段/ setter集群指定的名称代表对象的属性,当对象被视为黑匣子时,该名称是感兴趣的。
  • 您声明此属性的值可以通过对象的环境进行修改。
  • 您声明在此特定的具体类中,通过向其提交一些内存存储来实现对象的属性

我主张你永远不分青红皂白地让你的字段与getter和setter私密。字段用于描述存储。方法用于与环境交互。 (&#34; getters&#34;和#34; setters&#34;的特殊情况用于描述感兴趣的属性