究竟什么是封装?
我有两个封装的定义,它们不能适应一个定义。
- 封装是数据隐藏。通过使用私有、受保护和公共,将数据打包到单个组件中。
- 无论什么变化都会封装它。保护任何容易改变的东西。
这两个定义是如何谈论同一个概念的?
我有两个封装的定义,它们不能适应一个定义。
这两个定义是如何谈论同一个概念的?
封装可能是 OOP 中最容易被误解的概念。
封装不是数据隐藏!
“封装”来自“胶囊”。这意味着把东西放在一起,把它们关在一个包里,我们在这里谈论的“东西”是数据和函数。不进行封装的编程意味着处理数据的函数在代码中的某个位置“浮动”,尽管它们处理您的数据,甚至将该特定类型作为输入,但它们与您的数据是分开的。
让我举个例子,而不关注“公共”之类的东西:如果你有一个处理复数的类,它有一个实数和虚部,你可以简单地定义它::
class complex {
double real;
double imaginary;
};
使用例如在C中使用的旧的预封装样式,要获得此数字的绝对值,您将定义如下函数:
double absolute(double real, double imaginary);
这根本不会与班级联系起来!当然,您也可以定义一个将类复合体作为输入的函数,但它仍然是一个外部函数。因此,要使用它,您必须执行以下操作:
complex A;
A.real = 1;
A.imaginary = -3;
并获取您必须调用的绝对值
absolute(A.real, A.imaginary);
相反,您可以使用封装并将数据和函数放在一起:
class complex {
double real;
double imaginary;
double absolute(); // inside the class, encapsulated into it!
};
然后要获得绝对值,您只需调用该方法,例如
A.absolute();
这根本不需要隐藏数据。优点是代码更易于管理,因为您可以清楚地看到所有相关的“事物”(即数据和函数)组合在一起,因此您可以一目了然地知道您拥有什么(数据)以及您可以使用它做什么(方法)。
没有这个,信息隐藏是不可能的,因为这意味着你限制了从外部访问某些成员(私人成员),因此你必须在里面有一些方法,否则你将无法对你的数据做任何事情!
同时,信息隐藏有助于充分利用封装:如果人们可以从外部访问数据,那么让其他编码人员编写自己的(未封装的)代码来处理您的数据将面临非常高的危险,这至少会导致代码重复(即无用的工作)和不一致,如果实现不完全兼容。相反,数据隐藏意味着要访问私有数据,每个人都必须使用提供的公共方法,以便它们对每个人都是相同的。
因此,封装是数据隐藏有意义的必要条件,同时数据隐藏也有助于封装。它们在一起工作得很好,但它们不是一回事!
回到你的问题:鉴于此,定义1是错误的。正如CommuSoft所指出的那样,2并不是一个真正的定义,而是一个经验法则。我还要补充一点,这是一条经验法则,关于何时使用数据隐藏,而不是封装。
顺便说一句,Electrometro认为这可能是这个问题的重复。我认为值得注意的是,大多数答案都是错误的,包括顶部答案,它提供了一个封装的例子,实际上与封装相反。
如果你想要外部引用,这里有两篇关于此的文章:
抽象,封装和信息隐藏(请注意,当他开始一个名为“ENCAPSULATION”的段落并引用大量定义时,他只是试图展示围绕这个主题的混乱;这些定义是错误的,正如他稍后解释的那样!
隐藏数据是关于控制的。当你隐藏数据时,你对其他程序员(包括你未来的自己)隐藏了它,因为他们可能会对数据做一些不正确的事情,因为他们不像你那样了解数据。因此,您希望仔细控制他们如何使用数据 - 在这种情况下,通过隐藏数据,通过限制访问和可见性。
封装并不是真正“保护所有容易发生变化的东西”的做法。声称这一点的文章过于概括,因为大多数软件开发技术都是关于“保护”我们以后可能会改变的东西。你应该知道的是,检查容易改变的东西通常是一个好主意,并决定你想对其他人如何看待和使用这些东西施加什么样的限制 - 你如何封装这些东西。
...同样,封装也不是真正“将函数与数据放在一起”的做法,正如另一个答案中链接的几篇文章所声称的那样。正确的术语可能是模块化,或者简单的旧的面向对象编程 - 有许多相关的概念。这些技术可以带来封装,但将它们称为封装是令人困惑的,特别是对于初学者。封装实际上是关于“隐藏”数据。(举一个稍微高级的例子,在JavaScript中,你可以将一个值与函数放在一起,作为函数对象的属性,或者通过闭包。这些都不是使用字段,它们不涉及使用类/对象/原型的方法,都将数据和函数“放在一起”,但只有闭包会通过施加访问约束来封装数据。