# 封装隐藏——包装的讲究
# 封装
- 概念
所谓封装性(encapsulation, [ɪn,kæpsə'leɪʃən]),
就是将数据与相关行为包装在一起以实现信息隐藏(Information hiding)。
狭义的封装是在打包的基础上加上访问控制,以实现信息隐藏。
相对于广义的封装,不妨认为多了一个将白盒子刷成黑盒子的过程。
这个过程可以看做对抽象机制的一种语法上的补充和强化:
抽象意味着用户可以从高层的接口来看待或使用一类对象,而不用关系底层的实现,
而黑盒封装意味着用户不需要也无权访问底层的实现。
- 优势
分属不同类的函数是不可能产生歧义(ambiguity, [æmbɪ'gjuːɪtɪ])的,
哪怕是他们的签名一模一样。因此我们要把功劳记在封装的名下
# 访问控制
访问控制不仅是一种语法限制,也是一种语义规范——标有public的是接口,标有private的是实现,泾渭分明。
访问控制不是滴水不漏的,C++可以通过指针来间接访问private成员,Java和C#也可以利用反射机制暗渡陈仓。
但是:反射机制多用于单元测试、代码分析、框架设计等特殊用途,
在常规应用中,除了实例化编译器未知类外,一般不宜使用。
实际上,访问控制只是为了防止程序员的无意误用,不打算、也无法防止程序员的故意破坏。
# 安全问题
如果一个方法返回一个可变(mutable)域对象(field object)的引用,无异于前门紧闭后门洞开。
解决这个问题需要用到防御性编程,更具体的说是防御性复制,即返回对象的一个复制品,以免授人以柄。
如果一个域对象中又包含其他可变的对象,
简单的按位复制(bitwise copy,浅拷贝)仍然不够,需要其他的复制策略,如深拷贝或迟拷贝。
注:迟拷贝是浅拷贝和深拷贝的折衷方案:起初浅拷贝,必要时(如被修改)转为深拷贝。
常见形式是写时拷贝(copy-on-write,CoW)。
不是所有返回的域对象都会带来安全问题,请注意可变和引用两个条件。
基本类型都不是引用,作为传入参数或返回值时会自动被复制,因而是安全的;
不可变非基本类型,像Java的string、基本类型的包装类(wrapper class)如Integer、Double等,同样也是安全的。
此外C++和C#中申明了const的指针或引用返回值也能防止客户修改。
# 信息隐藏
信息隐藏原则固然重要,但也不是千遍一律的,比如仅作为数据存储的类甚至可以开放所有的域成员;
在非同类的对象之间共享或传递同一个引用有事也是必要的。
信息隐藏不仅是数据结构,还包括实现方式和策略(P214)。