【问题标题】:Why Encapsulation is called data hiding, if its not hiding the data?如果封装不隐藏数据,为什么称为数据隐藏?
【发布时间】:2015-08-13 05:37:24
【问题描述】:

以下两个类在数据隐藏(封装)方面有什么区别。

在下面的例子中,我可以通过公开成员的值来访问它。

例如:1

public class App {
   public int b = 10;

public static void main(String[] args) {
    System.out.println(new App().b);
 }
}

在下面的示例中,我可以使用 getter 方法访问成员的值。

例如:2

class DataHiding 
   {

    private int b;

    public DataHiding() {
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

    }

在上面的两个例子中,我都可以访问 member 的值。为什么例如:2,称为数据隐藏(封装)?如果它没有隐藏数据。

为什么 Eg : 1 不被称为封装?

【问题讨论】:

标签: java oop encapsulation


【解决方案1】:

如果您想检索 B 的状态而不能更改其值,会发生什么情况?您将创建 getter 而不是 setter,您无法通过将 B 作为公共 int 访问来实现。

此外,在 get 和 set 两个方法中,如果我们有一个更复杂的对象,也许我们想要设置或获取对象的某些属性或状态。

示例。

private MyObject a;

public setMyObjectName(String name){
     MyObject.name = name;
}

public getMyObjectName(){
     return MyObject.name;
}

这样我们通过限制对其状态的访问来保持对象的封装。

【讨论】:

  • 保持对象封装很好,我的问题是“为什么它被称为数据/信息隐藏”。我可以使用 setter 方法修改状态。
  • 乍一看对于 int 类型没有多大意义,但是如果 MyObject.name 需要修剪字符串怎么办?这样你就可以控制你的对象的使用方式,真正的变量是隐藏的。请注意,在这两种方法中,我只揭示了如何获取或设置对象的名称,也许它的另一个无法更改的特性被隐藏了。
【解决方案2】:

使用 mutatorsaccessors 隐藏了逻辑,而不是方法的名称。它可以防止用户直接修改类成员。

在您的第二个示例中,用户不知道类成员 b,而在第一个示例中,用户 直接 暴露于该变量,并且能够更改它。

想象一下,您想在设置b 的值之前进行一些验证,或者使用您不想公开的辅助变量和方法。您将把逻辑封装在 setter 中,这样可以确保用户在没有您监督的情况下无法修改变量。

【讨论】:

  • 我同意你的回答,用户不知道班级成员,但我的问题是它在哪里隐藏数据?我可以通过setter方法间接修改数据,为什么叫数据隐藏。
【解决方案3】:

在java中,所有的方法都是虚拟的。这意味着,如果您扩展某个类,则可以覆盖方法的结果。想象一下例如下一节课(继续你的例子):

class DataHidingDouble extends DataHiding{
  public int getB(){
    return b*2;
  }
}

这意味着您可以在子类中保持对 b 对外部世界的控制。 还想象一些子类,其中 b 的值来自不是变量的东西,例如。一个数据库。如果它是一个变量,那么你将如何让 b 返回值。 它隐藏了数据,而不是值。因为该类负责维护数据,并向外界返回正确的值。

【讨论】:

    【解决方案4】:

    封装不是数据隐藏,它是信息隐藏。您正在隐藏内部结构和数据实现,以及数据访问逻辑。

    例如,如果您愿意,您可以在内部将整数存储为String。在第一种情况下,更改该内部实现意味着您还必须更改所有依赖于b 的代码是int。在第二种情况下,访问器方法将保护内部结构并为您提供int,如果内部结构发生变化,您无需更改其余代码。

    除了普通的read-write 访问之外,访问器方法还使您有机会限制对数据的访问,使其成为read-onlywrite-only。更不用说可以验证进入对象的数据的完整性以及相应地更改对象状态的其他逻辑。

    【讨论】:

    • 这是正确的“更改内部实现意味着您还必须更改所有依赖于 b 的代码”。但我仍然可以使用 setter 方法修改信息,因此它不会隐藏信息。
    • 您对数据隐藏 术语的理解过于严格。 信息隐藏是更好的术语,更长的描述将隐藏内部实现细节并保护数据和对象完整性。
    【解决方案5】:

    它是关于什么的

    当您使用 和面向对象编程 标记此问题时,我想您正在隐含地考虑Java Beans。尽管如此,这是一个跨语言很常见的问题,请访问wikipedia 页面:

    在编程语言中,封装用于指代两种中的一种 相关但不同的概念,有时与组合1 其中:

    • 一种语言机制,用于限制对某些对象的访问 组件。
    • 一种便于捆绑的语言结构 具有对其进行操作的方法(或其他功能)的数据 数据。

    一些编程语言研究人员和学者使用第一个 单独或与第二个结合作为区分的意思 面向对象编程的特点,而其他编程 提供词法闭包的语言将封装视为 语言与面向对象正交的特征。

    第二个定义的动机是在许多 OOP 中 组件的语言隐藏不是自动的或可以被覆盖; 因此,信息隐藏被那些谁定义为一个单独的概念 更喜欢第二种定义。

    因此,封装实际上并不是隐藏数据或关于在语言组件中封装 数据片段的信息(Java 中的class)。 Java Beans 封装数据。

    话虽如此,虽然封装是面向对象编程范式的主要特征之一,但在语言设计历史的某个时刻,它被视为不足以帮助设计更好的软件。

    历史

    实现更好的软件设计的一个关键实践是decoupling,封装有助于解决这个问题。然而,cluster 的数据不足以帮助实现这一目标,当时在 OOP 开创性方面的其他努力是用不同的语言进行的,我相信 SIMULA 是最早引入某种visibility keywords 等概念的语言班级。然而,信息隐藏的想法后来在1972 中真正出现,其数据仅与使用它的组件相关,以实现更大的解耦。

    但回到主题。

    回答您的问题

    1. 在这种情况下,数据被封装并公开

      这通常被称为全局变量,它通常被认为是一种不好的编程习惯,因为这可能会导致耦合和其他类型的错误

    2. 数据被封装和公开(通过方法访问器)

      这个类通常被称为 Java Bean,如果使用它们的设计用途之外的任何其他用途,这些都是可憎的。

      这些对象被设计为履行单一角色,根据specification,这是非常具体的

      2.1 什么是 Bean? 让我们从一个初始定义开始,然后对其进行细化:

      “Java Bean 是可重用的软件组件,可以在构建器工具中进行可视化操作。”

      为什么现在它是可憎的?因为人们,框架供应商通常会滥用它们。规范对此还不够清楚,但在这方面有一些声明:

      因此,例如,将 JDBC 数据库访问 API 作为类库而不是 bean 来提供是有意义的,因为 JDBC 本质上是一种编程 API,而不是可以直接呈现以进行可视化操作的东西。

      我宁愿引用 Joshua Bloch(更多内容请参见 question and answer):

      “JavaBeans 模式有严重的缺点。” - Joshua Bloch,有效的 Java

    相关要点

    如上所述,实现更好软件的一个关键做法是解耦。耦合一直是软件工程师最古老的战场之一。封装、信息隐藏与以下有助于解耦的做法有很大关系,原因有很多:

    • the Law of Demeter,打破这个规律意味着代码有耦合。如果必须手动遍历整个数据图,则没有信息隐藏,图的知识在组件之外,这意味着软件因此难以维护,适应性更差。简而言之:重构是一个痛苦的过程。 Anemic domain model 受此影响,它们被认为是反模式。

      一种不违反得墨忒耳法则的现代实践是Tell, Don't Ask

      也就是说,你应该努力告诉对象你想让他们做什么; 不要问他们关于他们的状态的问题,做出决定,然后告诉他们该怎么做。

    • immutability,如果数据必须公开,它应该是不可变的。在某种程度上,如果不需要数据,一个模块可能会在另一个模块中引入副作用;如果这对于单线程程序是正确的,那么对于多线程软件来说就更痛苦了。今天软件和硬件越来越多线程,线程必须通信,如果信息必须公开,它应该是不可变的。不变性保证了线程安全,少了一件需要担心的事情。此外,还必须保证整个对象图的不变性。

      class IsItImmutable {
        // skipping method accessors for brevity
      
        // OK  <= String is immutable
        private final String str;
      
        // NOK <= java.util.Date is mutable, even if reference is final a date can be modified
        private final Date  date;
      
        // NOK <= Set operations are still possible, so this set is mutable
        private final Set<String> strs;
      
        // NOK <= Set is immutable, set operations are not permitted, however Dates in the set are mutable
        private final Set<Date> udates = Collections.unmodifiableSet(...);
      
        // OK  <= Set is immutable, set operations are not permitted, String is immutable
        private final Set<String> ustrs = Collections.unmodifiableSet(...);
      }
      

    【讨论】:

      猜你喜欢
      • 2019-09-28
      • 2013-09-29
      • 2018-06-29
      • 1970-01-01
      • 2016-05-15
      • 2021-07-24
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      相关资源
      最近更新 更多