静态常量是否违反"encapsulation"?

Does static constants violate "encapsulation"?

好的,所以我正在使用这本书:核心 Java 第一卷——基础知识。

封装定义如下:

Encapsulation (sometimes called information hiding) is a key concept in working with objects. Formally, encapsulation is simply combining data and behavior in one package and hiding the implementation details from the users of the object.

在SO里查了一下,知道封装和信息隐藏是两个独立的概念,是结合在一起使用的。但是,为了这个问题,让我们坚持书中的定义(说封装==实现隐藏),因为这个问题使用了书中的例子。

public class Math
{
 . . .
 public static final double PI = 3.14159265358979323846;
 . . .
}

书上说这不会破坏封装,因为它是一个常量。但是上面的代码不会破坏封装的 实现隐藏 部分(根据本书的定义),因为 PI 不仅对 class 可见,而且对程序的其余部分可见。

我的问题实际上可能与此重复:Does a public static const variable break the encapsulation ideology?(虽然用 C++ 标记)但答案说它确实违反了封装(与书相矛盾),这没关系。我知道我的问题是否因为这个可能的重复问题而被关闭

编辑:我只是 post 另一个示例代码作为评论提到的 PI 不被视为实现细节

public class System
{
 . . .
 public static final PrintStream out = . . .;
 . . .
}

我认为静态不符合 OOP 原则。所以既不违也不守。

静态文字不是任何对象的一部分,它们只是分配给全局变量的数据以供说服。 因此,这本身并没有违反封装,而是完全没有面向对象。
OOP 方法是根本没有全局常量,而是定义一个对象来解决您的问题。

interface Figure {
    double perimeter();
}
class Circle implements Figure {
    private final double radius;

    @Override
    public double perimeter() {
        return this.radius * 2 * (float) 3.1416926; 
    }
}

以下是关于该主题的更多讨论: https://www.yegor256.com/2015/07/06/public-static-literals.html

好问题。我不认为你提供的例子破坏了封装,至少严格来说是这样。

你给出的第一个例子是常量 PI,第二个例子是提供对 System 中 out 常量的访问的例子;大概是为了利用一些代码,例如 System.out.println("HelloWorld!");。正如其他人已经提到的,PI 实际上只是一个常量,常量的用户无法修改或影响该值。 PI 的用户仍然需要在他们的代码中引用常量(构成这里的 API)。如果 PI 被修改(不太可能,但谁知道呢)用户将不会受到该更改的影响,因为无论如何他们都需要重新编译代码。

考虑封装的一个有用方法是考虑为了违反封装需要什么。 Effective Java 第 3 版在第 16 项中很好地描述了这一点,建议如果没有适当的封装“你不能在不改变 API 的情况下改变表示,你不能强制不变量,你不能采取访问字段时的辅助操作。"

一个非常明显的违反上述内容的是下面的 class(也在 Effective Java 的第 16 项中):

class Point {
  public double x;
  public double y;
}

所有字段都是 public,此 API 的用户将被迫直接使用这些字段。如果后来作者决定在可以访问或修改 xy 之前添加一些验证检查,那么在不破坏现有客户端的情况下就不可能这样做。 API 必须进行重大修改,并且可能会破坏下游用户的行为。

现在让我们看看您提供的第二个示例:

public class System
{
 . . .
 public static final PrintStream out = . . .;
 . . .
}

这看起来与上面 PI 的示例非常相似,但有一个关键区别:所讨论的字段是 PrintStream。虽然 out 字段本身是此 API 的明确部分,现在可能很难更改(现在它已公开,客户将使用并依赖它),但 PrintStream 类型是一个 class,这实际上是这里有趣的地方:用户将在 class 上引用方法。这里我们有 PrintStream 的 API 中的关键功能,可以在不中断使用的情况下随着时间的推移而发展。将来也有可能将 out 常量更改为引用不同的 PrintStream subclass 并且 API 的用户应该不受影响。

希望对您有所帮助。