(译)在OOP中抛弃封装

注意:本文中术语‘Encapsulation’是作为一种限制对象访问的语言机制。其他人可能把这个称为‘信息隐藏’

 

Traditional Object Oriented Programming

 

     在每一堂OO编程的课程或者每一本OO编程的书中,你都会知道一个叫做封装的概念.封装是一个保护实现细节,并且为了访问对象,仅可以访问向外提供的接口。封装的好处是只能调用公开的接口,而不依赖于具体的实现细节。

     封装背后的思想是:你可以保护类不会被错误地使用。你可以确保用户不会破坏你的类。你可以全面的掌控你的类,同时用户只可以调用你允许的部分。另外一个优点就是:由于向外提供了一个稳定的接口,这样就可以保护内部的细节不会被修改。而且也可以减少软件组件之间的耦合。是的,封装可以强制用户来达到这样的目的。

大多数的OO编程人员也认同上面的所有的观点,包括我,但是当我遇到了python后我改变了想法.

 

OOP without Encapsulation?

 

      Python支持面向对象的思想,但是不支持封装。这怎么可能?无数的OO程序员在python的用户组上提出相同的问题,最后结论是相当的简单:你根本不需要在OO中加入封装。在pyhon中,所有的都是public,如果你不需要公开一些成员或者方法,你只需要在它们前面加入下滑线.如果用户去使用它们,那是他们自己在冒险。Python程序员把这个称为“约定封装大于强制封装”.但是从本质上来说这个不是封装(信息隐藏)。如果你问为什么这是一个进步,因为在一些极少数的情况下,能够访问内部的细节是必不可少的。

下面我来解释一下:

 

Using the ‘computer’ metaphor(以电脑为例)

 

     我的桌上有一台电脑,它具有良好的内部引擎。它有简单清爽的对外接口; 前面有两个按钮“Power” 和 “Reset”,一些LED灯来指示硬盘是否工作。后面也有一些接口,来连接电源,网络,键盘,鼠标,显示器等等。如果电脑坏了或者我想升级,我只要买一个的新的零件就可以了,而且这些零件和旧的零件有相同的接口。

     我的电脑根本没有采用封装。请记住封装是限制对底层组件的访问。但是对于我的电脑,我可以打开机箱盖然后做任何事情。电脑厂家并没有限制我这样做,我也乐意厂家这样。核心的问题是:当我打开机箱盖的时候。我清楚下面两点:

     1. 也许会破坏电脑的正常工作。

     2. 如果我买一个新的零件,那么新的零件可能不会正常工作。

     这两点正是封装要阻止的。但是我必须做出选择。我要想能够有一个新的硬盘或者风扇,当然,我知道风险,而且我的电脑也很可能不支持新的零件。但是作为电脑用户,我的职责是为解决问题而做出正确的选择。

     如果电脑厂家采用了封装,那又如何呢?电脑的机箱盖不能被打开,如果我们要换零件那么必须要到电脑店中。

     封装限制了对细节实现的访问,当然我不是说你必须要去访问实现的细节,但是有的时候我们需要能够很方便地访问细节。对于电脑,我很乐意厂家没有限制我可以打开机箱盖,对于软件,我是怎么做的呢?

 

Abstraction instead of Encapsulation

 

    抽象的定义为:” the act of considering something as a general quality or characteristic, apart from concrete realities, specific objects, or actual instances”.在编程领域,这就意味着在一个具体的类上面定义一个接口,用户仅仅需要知道接口(这就是抽象),用户不用知道具体的实现细节。这和封装不同,如果用户需要能够访问底层的细节,他依然可以访问底层的细节。

     大多数的硬件厂商使用抽象来取代封装。它们提供一个不可变的接口,但是”有权限的用户”依然可以打开机箱盖来访问内部的细节。

     如果用抽象替代封装,那么用户可以非常清楚地看到公开的接口,而且和采用封装的一样--具有相同的行为。用户被鼓励使用接口,但是并不局限于此。如果仅仅采用接口(抽象),同样能够得到封装带来的好处。但是额外的好处是:如果需要我们可以访问底层的细节。而且因为接口和实现泾渭分明,用户清楚其中的风险(比如打破兼容性)。

 

A real world programming example

 

     我以实际中的编程为例来说明抽象是如何代替封装的.假设我正在构建一个应用程序,该应用程序调用了一个跨平台的GUI库。该库非常优秀,因为可以在多个平台上移植。在不同的平台,有这相同的公共接口,所以在不同的平台迁移的时候那些方法不需要修改代码。

     现在假设一个Window平台的用户需要一个新的特性,这个特性这个库没有提供。但是可以调用Windows底层的实现来完成(例如MFC支持该特性).

    这个时候你面临一个选择:如果遵循封装的原则,你有如下的限制:

    1. 在该库上自己实现该特性,这可能意味着重新写整个组件库(重复GUI整个库的代码,同时加入MFC中该特性的代码)

    2. 联系供应商,然后让它们来实现.这意味着供应商必须在所有的平台上实现该特性。也意味着,一时半会你拿不到新的版本。

    3. 把整个代码迁移到Windows特定的库,这个的花费大量的时间,金钱。

如果你抛弃封装,采用抽象呢?你依然有上面的三种选择,但是多了一个选择。你可以访问底层的细节,但是同时需要明白这样做的风险:

    1. 破坏该GUI库的其他部分

    2. 该库将来可能会改变调用MFC的方式,所以以后你升级该GUI库的时候需要特别小心。

    3. 没有官方的支持

     结果是,采用抽象而不是封装,利大于弊。在本例子中,也许你是对的,但是我不是鼓励去访问底层的细节。但是在一些特定的情况下,这种做法也许是最佳选择。

 

How I do it

 

     因为Python不支持封装。用抽象来替代封装就顺其自然.我遵循如下的约定:对于不公开的成员,在前面加下划线(译者注:这是python的语法约定)我的用户仅仅调用我公开的接口,如果他们确实需要访问实现的细节,那也很容易。

     在更多的传统的面向对象的语言中,我用public来修饰接口,用protected来修饰实现的细节。你可能知道下面的四人帮的表述:”因为继承向子类曝露了父类的实现细节,所以继承违背类封装的原则”.而这正是我想通过抽象达到的目的。我的用户可以调用我公开的接口,它们也可以通过继承来访问实现的细节。这种做法加快了我的开发速度,因为我不想让更多的时间耗费在是选择private?还是选择package private?还是选择protected?还是选择public?我喜欢这种简单而愚蠢的选择。

 

Encapsulation is not Security

 

     有的时候阻止用户访问一些内部数据时必要的。其实这个时候我们是在考虑安全问题,而实现安全特性的最差的方式就是OO中的封装。

 

Conclusion

 

    依赖于具体的实现并不可取。因此你必须仅仅调用类的接口。但是对于一些特定的应用或者不可知的需求。能够访问实现的细节是有用的。实现更多的功能是类的职责,以一种专业的方式来使用类是程序员的职责。不要认为用户是白痴,为了解决问题,他们有足够的智慧以一种最佳的方式来调用你的类。如果用户需要能够访问实现的细节,它们可能会去这么做,他们不会做出别的选择。所以不要以封装作为理由,实际把用户看出白痴。

作者:Koen Witters

原文地址

译者注:本人并不同意文中关于不需要封装的观点。当初翻译该文的原因是被其新奇的观点吸引,但是当我明白其中的意思以后。我后悔了,翻译该文花费了我大量的时间。我不知道Phython为什么要这样做。关于封装的好处,该文的论述有点不足,关于这点,原文的评论中提到。

转载于:https://www.cnblogs.com/ptwlw/archive/2010/05/09/1731159.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值