封装和信息隐藏是密切相关的概念,尽管它们的精确定义取决于您与谁交谈。
“信息隐藏”的概念首先由Parnas (1972)描述,他建议应该限制对信息的访问,以减少系统的互连性。他建议这将有助于将系统拆分为模块,同时保持用户友好的外部界面,并允许在不影响客户端的情况下更改实施细节。
Zilles (1973)创造了术语“封装”来描述使用过程来控制对底层数据的访问,以降低系统复杂性并保护数据免受危险修改。
随后,Parnas (1978)将信息隐藏和封装(和抽象)描述为同义词,它们描述了隐藏可能更改的系统细节。然而,信息隐藏和封装之间已经有了区别,例如Micallef (1987)将封装描述为“对信息隐藏的严格执行”。一些作者,例如Cohen (1984)和Abreu 和 Melo (1996)将“封装机制”描述为允许信息隐藏,特别是在面向对象的编程语言中。
Meyers (2000)提出,一段代码的封装程度取决于如果它发生变化会被破坏的代码量。从这个意义上说,私有数据和方法被封装得越多,访问它们的方法就越少。相比之下,公共数据和方法是完全未封装的,因为可以访问它们的代码量是未知的。
相反,Rogers (2001)认为封装只是一种语言机制,它允许将数据与操作该数据的方法捆绑在一起。他声称封装从根本上与信息隐藏无关。然而,这一定义与他的文章发表之前的 28 年学术文献中对该术语的几乎所有用法背道而驰。还有其他一些这种用法的例子,例如Archer 和 Stinson (1995),但它们很少而且相差甚远,也不是特别引人注目。
总之,信息隐藏是指信息应该被隐藏,以便可以在不影响客户的情况下更改设计。这允许增加灵活性和安全性。封装可能被认为与信息隐藏相同,但该术语通常用于描述信息隐藏的实际实现,尤其是在面向对象的编程中。
作为信息隐藏/封装的一个例子,考虑这个类:
public class BankAccount {
public int dollars;
}
这个类的实现是完全未封装的,这意味着它不灵活(例如我们以后不能轻易添加对单个美分的支持)并且不安全(例如可以将帐户更改为负数)。但是,如果我们将数据隐藏在正式定义的方法接口后面,我们将获得灵活性和安全性。
public class BankAccount {
private int dollars;
public void deposit(int dollars) {
this.dollars += Math.max(0, dollars);
}
}
我们现在可以控制如何修改状态,并且我们还可以在不破坏客户端代码的情况下更改实现:
public class BankAccount {
private int cents;
public void deposit(int dollars) {
deposit(dollars, 0);
}
public void deposit(int dollars, int cents) {
this.cents += Math.max(0, 100 * dollars) + Math.max(0, cents);
}
}
该类现在被更好地封装,因为我们隐藏了有关其底层实现的信息。