在数据隐藏(封装)方面跟随两个类有什么区别。
在下面的示例中,我可以通过将其公开来访问成员的值。
例如:1
public class App {
public int b = 10;
public static void main(String[] args) {
System.out.println(new App().b);
}
}
在下面的例子中,我可以使用getter方法访问member的值。
例如:2
class DataHiding
{
private int b;
public DataHiding() {
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
}
在上面两个例子中,我都可以访问member的值。为什么Eg:2,被称为数据隐藏(封装)?如果它没有隐藏数据。
为什么Eg:1不被称为封装?
答案 0 :(得分:2)
当您使用java和面向对象编程oop标记此问题时,我认为您隐含地考虑了 Java Bean 。然而,这是一个跨语言相当常见的问题,请参阅此问题的wikipedia页面:
在编程语言中,封装用于指代两种中的一种 相关但不同的概念,有时候是组合1 物:
- 限制访问某些对象的语言机制 组件。
- 促进捆绑的语言结构 使用方法(或其他功能)操作的数据 数据。
一些编程语言研究人员和学者使用第一种 单独或与第二个结合作为区别的含义 面向对象编程的特性,而其他编程 提供词法闭包的语言将封装视为一种 与面向对象正交的语言的特征。
第二个定义的动机是许多OOP 隐藏组件的语言不是自动的,也不能被覆盖; 因此,信息隐藏被那些人定义为一个单独的概念 更喜欢第二个定义。
因此,封装并不是真正隐藏有关将数据包含在语言组件中的数据或信息(Java中的class
)。 Java Beans 封装了数据。
话虽如此,虽然封装是面向对象编程范式的主要特征之一,但在语言设计历史的某些方面,它被认为不足以帮助设计更好的软件。
实现更好的软件设计的一个关键实践是decoupling,封装有助于解决这个问题。然而,cluster的数据还不足以帮助实现这一目标,当时OOP开创性的其他努力是用不同的语言进行的,我相信SIMULA是最早引入某种visibility keywords的语言。其他概念,如课程。然而,信息隐藏的想法后来真正出现在1972中,其数据仅与使用它来实现更大解耦的组件相关。
但回到主题。
在这种情况下,数据被封装并公开
这通常被称为全局变量,它通常被认为是一种糟糕的编程习惯,因为这可能导致耦合和其他类型的错误
数据被封装并公开(通过方法访问器)
这个类通常被称为 Java Bean ,如果用于除了它们之外的任何其他内容,这些都是令人厌恶的。
这些对象旨在实现单一角色,而且根据specification
非常具体2.1什么是Bean? 让我们从初始定义开始,然后对其进行优化:
“Java Bean是一个可重用的软件组件,可以在构建器工具中以可视方式进行操作。”
为什么现在这种憎恶?因为人们,框架供应商通常会滥用它们。规范还不够明确,但在这方面有一些陈述:
因此,例如,将JDBC数据库访问API作为类库而不是bean提供是有意义的,因为JDBC本质上是一个编程API而不是可以直接呈现用于可视操作的东西。
我更喜欢引用Joshua Bloch(更多信息question and answer):
" JavaBeans模式存在严重的缺点。" - Joshua Bloch,Effective Java
如上所述,实现更好软件的一个关键实践是解耦。 Coupling一直是软件工程师最古老的战场之一。封装,信息隐藏与以下实践有很大关系,以帮助解耦,原因有很多:
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(...);
}
答案 1 :(得分:1)
使用 mutators 和访问者隐藏逻辑,而不是方法的名称。它阻止用户直接修改类成员。
在第二个示例中,用户不知道类成员b
,而在第一个示例中,用户直接暴露于该变量,具有更改的能力它
想象一下,在设置b
的值或使用辅助变量和您不想公开的方法之前,您想要进行一些验证的情况。您将逻辑封装在 setter 中,通过这样做,您可以确保用户无法在没有您监督的情况下修改变量。
答案 2 :(得分:1)
封装不是数据隐藏 信息隐藏。您正在隐藏内部结构和数据实现,以及数据访问逻辑。
例如,如果您愿意,可以在内部将整数存储为String
。在第一种情况下,更改内部实施意味着您还必须更改依赖b
为int
的所有代码。在第二种情况下,访问器方法将保护内部结构并为您提供int
,如果内部已更改,则无需更改其余代码。
除了普通的read-only
访问权限外,访问者方法还可以限制对write-only
或read-write
数据的访问权限。更不用说其他逻辑可以验证进入对象的数据的完整性以及相应地改变对象状态。
答案 3 :(得分:0)
如果您想要检索B的状态而不能更改其值,会发生什么?你会创建getter而不是setter,你不能通过访问B作为公共int来实现它。
此外,在get和set方法中,如果我们有一个更复杂的对象,也许我们想要设置或获取对象的某些属性或状态。
实施例
private MyObject a;
public setMyObjectName(String name){
MyObject.name = name;
}
public getMyObjectName(){
return MyObject.name;
}
这样我们通过限制对其状态的访问来保持对象的封装。
答案 4 :(得分:0)
在java中,所有方法都是虚拟。这意味着,如果扩展某个类,则可以覆盖方法的结果。想象一下,例如下一堂课(继续你的例子):
class DataHidingDouble extends DataHiding{
public int getB(){
return b*2;
}
}
这意味着您可以控制子类中外部世界的b。 想象一下一些子类,其中b的值来自不是变量的东西,例如。一个数据库。如果它是一个变量,你如何使b返回值。 它隐藏数据,而不是价值。因为该类负责维护数据,并将正确的值返回给外界。