4

在封装中,想法是通过将变量声明为私有来隐藏类对其变量所做的事情,从而实现 OCP。但是,为什么你要添加 getter 和 setter,然后打开你的变量进行修改?

为什么要经历将变量设置为私有的整个麻烦,然后在它们上添加一个公共设置器?这似乎不是很严格,这是封装的思想之一。

4

7 回答 7

5

打开你的变量进行修改

开闭原则中,“开放”这个词意味着完全不同的东西。

这意味着类的源代码对修改是关闭的,但生成的类工件是开放的,可以通过扩展进一步修改。开放封闭都不是指类实例的任何方面。

另一方面,我和你一起从一个稍微不同的角度批评 getter/setter:他们添加了大量的样板,只是为了将你的整体设计恢复到与具有公共字段的类几乎相同的东西。

某些不太常见的情况下,给予 getter/setter 仍然是值得的。这对于属于库的公共 API 的类是正确的,并且通常对于通过设置器配置的具有一些非平凡行为的任何类都是如此。

对于纯数据类,getter/setter 主要是由于拒绝使用公共字段的过时框架的要求而被迫进入其中的。Spring、Hibernate 和 Jackson 是不强制执行此操作的最先进框架的示例。

于 2013-12-17T12:08:27.040 回答
3

直接设置变量可以控制外部代码,例如

class A {
    public String a;
}
A a = new A();
a.a = "bad value which will cause code fail"

但在 getter 和 setter 的情况下,

class A {
    private String a;

    public void setA(String a) {
       if(! "some bad value".equals(a) ) {
           this.a = a;
       }

    }
}

因此,使用 getter 和 setter 将使用我们的类实例而不是其他可能的坏类来控制。

于 2013-12-17T12:09:21.480 回答
3

给你。
假设这是我的实例变量

private double x;

通过使用 Setters,您可以对将值设置为实例变量进行自己的验证。

public void setX(double x)
{
    if (x <= 0)
        System.out.println("Wrong value, value should be greater than 0");
    else
        this.x = x;
}

通过使用 Getter,您将能够以您希望用户看到的格式返回数据。

public double getX()
{
    return Double.valueOf(String.format("%.3f", this.x));
}
于 2013-12-17T12:12:10.517 回答
2

封装是关于隐藏实现细节。

您的 getter 和 setter 方法是您的类的已发布 API 的一部分,并提供了一种明确定义的方式来查看正在发生的事情。

在内部,您可以删除所有实现并做一些完全不同的事情,但是通过保持相同的 set 和 get 方法,这种更改对所有其他类都是不可见的。

API 应该是关于使用它的人想要做的事情,而不是你如何做的内部细节。

例如,如果您有一个连接到服务器的类,并且有一个包含名称和端口的字符串,并且现在公开该字符串,那么您就被绑定了。

如果将来您想将其拆分并分别存储名称和端口,则不能这样做,因为人们可能会直接读取或设置该字符串-您将破坏所有这样做的人的代码。

或者更糟糕的是,假设你保持它的存储不变——但你需要知道它什么时候发生变化。这样做是不可能的(实际上有许多 hacky 方法,例如存储上次查看的字符串并检查更改,或者运行线程以查找更改等,但所有这些方法都是 hacky 且效率低下)

于 2013-12-17T12:17:07.120 回答
1

一般来说,你应该分别设计一个类的接口,即你希望你的类如何被使用,它的实现,即它是如何工作的。通过这样做,您的某些方法可能最终只做设置或获取属性值的事情。

但是,您应该仅在有意义而不是作为一般方法时才这样做。

真正没有任何意义的是只有私有属性的类,每个属性都有一个 setter/getter 公共方法,没有其他方法。在某些情况下,您确实需要将几个值一起移动,而没有任何绑定它们的特定逻辑;在这种情况下,您应该定义一个只有公共属性而没有方法的类,除了可能的构造函数。

于 2013-12-17T12:37:16.670 回答
1

您公然向每个私有成员添加 setter/getter 是绝对正确的,这违反了封装和 OCP。

为了解释这个悖论,OCP 需要一些上下文(即,相对于什么是封闭的?)。在类设计级别,OCP 意味着如果您更改(私有)实现细节,则(公共部分)类的客户端不必更改。相对于使用封闭(公共)接口的客户端,类的私有部分可以更改。

为每个私有变量添加 getter/setter 将违反 OCP。这与将每个私有属性设为公共属性相同(除了验证方面,其他一些答案已经提到,但在我看来这些都不是重点)。有关为什么这不好的更多解释,请参阅为什么 getter 和 setter 方法是邪恶的

与其根据实现细节(getter 和 setter)设计方法,不如尝试将类的公共接口视为类提供给系统的一组“服务”或“职责”。如果可能,这些服务应该隐藏实现细节。想想班级的客户将如何使用它。在责任驱动设计中查看更多信息。

至于 getter 和 setter,它们并不总是坏的。在添加它们之前请三思而后行。公共方法是对您支持服务的类的客户的一种承诺(如果您更改公共接口,使用该类的客户将中断)。如果您在公共界面中显示课程的详细信息,您可能会更难更改课程的详细信息。

当您(通过 get)返回对作为实现细节一部分的对象的引用时,还有另一个角度需要考虑。获得该引用的客户端可能会操纵该对象(get 揭示了实现细节)。例如,Student.admissionDate:Date有一个返回对 Date 的引用的 getter。Student 类的客户可以获取日期并更改它。为避免这种情况,getter 应返回对象的克隆。在Java中它将是return (Date)admissionDate.clone()

于 2014-03-23T16:02:38.890 回答
1

通过设置 setter 和 getter,您可以

  • 隐藏实际实现(并非每个属性都必须由变量备份,也可以是计算属性或转换属性,或者从其他地方读取/写入值)
  • 限制访问,只为您想要公开的那些属性提供 getter,并且只为您希望允许修改的属性提供 setter
  • 控制访问,例如在值不正确或访问权限不足时拒绝值
  • 添加观察者支持,通过保护状态变化并在发生变化时触发变化事件
  • 读取/)写入值时触发逻辑
  • 以及更多。
于 2013-12-17T12:14:57.663 回答