分析有关封装和信息隐藏的误区

封装(Encapsulation)和信息隐藏,这两个词,我想大家都不会陌生。但是,有很多开发人员并没有深入的了解,甚至存在一些误区。今天就专门来说一下。

一、封装

1、什么是封装?

从字面意思来看,封装就是把一些相关的 东西打包成一坨(看到“坨”这个量词,不要想歪了)。“封装”最广为人知的例子,就是在面向对象编程(以下简称OOP)里面,把数据和针对该数据的操作,统一到一个class里。

2、封装有啥好处?

那封装有啥好处捏?一个主要的好处,就是增加软件代码的内聚性。通过增加内聚性,进而提高可复用性和可维护性。

3、封装的手段

很多人把封装的概念局限于类,认为只有OO中的class才算是封装。这实际上是片面滴!在很多不使用"类"的场合,一样能采用封装的手法。下面俺随手举几个和OO无关的例子:

a、通过文件

比如C和C++支持对头文件的包含(#include)。因此,可以把一些相关的常量定义、类型定义、函数声明,统统封装 到某个头文件中。

b、通过namespace/package/module

C++的namespace、Java的package、Python的module,想必各自的开发人员都很熟悉。这些语法虽然称呼各不相同,但具有相同的本质。因此,也可以利用这些语法,来进行封装。

二、信息隐藏

1、什么是信息隐藏?

“信息隐藏”——顾名思义——就是把不该暴露的信息藏起来。说到信息隐藏,很多人自然而然地,就联想到某些OO语言(比如C++、Java)提供的,诸如private、protected之类的关键字。这些关键字可以通过访问控制,来达到信息隐藏的目的。

2、信息隐藏有啥好处?

信息隐藏的好处,正好和“封装”的好处相呼应。封装是为了提高内聚性;而信息隐藏是为了降低耦合性。通过降低耦合,一样可以达到提高可复用性、可维护性这2个目的。

3、信息隐藏的手段

和封装类似,很多程序员也把信息隐藏的概念片面化——认为信息隐藏仅限于private和protected关键字。所以,俺再随手举几个其它的信息隐藏手段。

a、通过接口类

可以通过定义接口类(Java中的interface、C++中的纯虚类)来实现信息隐藏。具体实现如下:

定义一个接口类,仅包含一些公有的成员函数的声明 (Java的abstract函数,C++的纯虚函数),没有任何函数实现,也没有任何成员变量。然后把具体的实现代码放到该接口类的一个派生子类中。

由于调用者只看到接口类,看不到实现类。所以,同样可以达到信息隐藏。在某些情况下,使用这种手段达到的效果,会比基于访问控制(使用private关键字)的效果,更好。

不过这种手段依赖于语言的支持。使用该手法的编程语言,至少要支持继承、虚函数等语法。

b、通过pimpl手法

pimpl手法也叫作“Opaque Pointer”手法。和接口类的手法不同,pimpl手法不需要靠继承、虚函数等语法的支持,因此对诸如C语言来说,很有用。

三、一些理解上的误区

介绍完一些基本概念,再来说一下,关于封装、信息隐藏的一些常见误区。

1、把封装等同于信息隐藏

这是混淆最严重的一个误区——很多初学OOP的同学都把封装和信息隐藏混为一谈。希望经过俺前面的一番解释,有些人能搞明白其中的差异。顺便提一下,有个老外写了篇小有名气的文章——“Encapsulation is not information hiding ”。洋文好的同学,可以去瞅一瞅。

2、把封装看得太狭隘

其实前面已经通过举例,驳斥了狭隘看待封装的误区。此处不再啰嗦。

3、把信息隐藏看得太狭隘

前面已经通过举例,驳斥了狭隘看待信息隐藏的误区。此处不再啰嗦。

4、混淆可访问性和可见性

考虑到某些网友可能连这两者的区别都不太清楚,先简单解释一下。所谓可访问性,就是可以对某个东西进行读/写操作;所谓可见性,就是能够感觉到某个东西的存在。

前面谈到信息隐藏时,我们提及了通过访问控制的关键字(private、protected)来达到信息隐藏的目的。有很多同学认为这几个关键字不光禁止了可访问性(accessibility),还禁止了可见性(visibility)。其实也不尽然。不同的编程语言,对这两者的处理是不同滴。比如在 C++语言中,类的私有成员虽然不可访问,但还是可见的。此话怎讲捏?请看下面的例子:

 
 
 
  1. int n = 0;  
  2. class Parent  
  3. {  
  4. public:  
  5. Parent()  
  6. {  
  7. n = 1;  
  8. }  
  9. private:  
  10. int n;  
  11. };  
  12. class Child : public Parent  
  13. {  
  14. public:  
  15. Child()  
  16. {  
  17. }  
  18. void Func()  
  19. {  
  20. ::printf("%d", n);  
  21. }  
  22. };  
  23. int main()  
  24. {  
  25. Child c;  
  26. c.Func();  
  27. return 0;  
  28. }  

如果俺问列位看官,程序执行后会打印出啥?相信一多半的同学会回答:“打印0”。

但是,真相是:该程序根本在编译时就报错了。

问题在于,父类的私有成员变量n虽然对子类是无法访问的,但依然是可见的(可感知的)。所以,对于那个printf语句,编译器会认为是企图访问父类的私有成员,故而报错。

再悄悄跟大伙儿说一下,这个例子是俺从C++它爹所写的《The Design and Evolution of C++》里面剽窃滴。

四、结尾

今天的话题,基本上到此结束了。***,俺想提醒列位看官注意一下:象封装和信息隐藏,都属于编程的基本功,为啥很多开发人员都没有想透彻捏?

如果大家有什么不明白的,可以参见原文,下面是原文地址。

本文出处:http://blog.csdn.net/program_think/archive/2010/08/29/5846881.aspx

【编辑推荐】

  1. Javascript面向对象编程(一) 封装
  2. 基本的封装Ajax之一
  3. 浅谈C++调用C#的DLL程序方法
  4. C++多态技术的实现和反思
  5. C++连接mysql数据库的两种方法

名称栏目:分析有关封装和信息隐藏的误区
文章链接:http://www.shufengxianlan.com/qtweb/news28/319428.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联