设计的核心任务之二:信息隐藏
假使说我们认同软件的构造是一个复杂的过程,那么管理这种复杂度必然需要一些技巧。而为了找出这些技巧,则需要先瞄一眼这种复杂度的基本构成。
软件的构造过程牵涉了两个最为基本的要素:一是软件,一是构造软件的人。
假设说存在着一个标准的人,这个人智力水平恒定,创新能力恒定,技能水平恒定。那么软件的复杂度只决定于其自身,比如软件所需要面对的业务规则,所需要的计算水平等。应对这类复杂度的有效手段是优化方法,好比说快速排序的效率大多时候就是比冒泡排序好。
当我们开始考虑人的可变因素时,复杂度的来源则发生了变化。人是有着许多与生俱来的特质的,比如说:人是会犯错的,人同一时间可以处理的事情是有限度的。因为这些特性,人在应对软件自身所拥有的复杂度的同时,又带来了偶然的,与个人特质相关的复杂度。比如说:局部变量的名称可能碰巧和一个全局变量相同,而我们又误把这个局部变量作为全局变量使用了。
信息隐藏则是减少这类偶然性复杂度的一个有效手段。
对于方法或函数而言,输入参数,输出参数描述了它的边界。
对于类而言,公有属性和公有方法描述了它的边界。
边界以内的即是被隐藏掉的信息。
假设说我们用一个名为CustomerManager的类来管理客户的基本信息,但在开放出AddCustomer(), EditCustomer(),DeleteCustomer()这样接口的同时,也开放了内部存放客户信息的集合类。那么使用CustomerManager类的人,很可能直接通过操作集合类来获取客户信息。这个做法虽然可以被编译器所接受,但实际上是对CustomerManager类的误用。为了避免这类误用,存放客户信息的集合类应该被隐藏起来,让用的人只看到接口。
信息隐藏是抽象数据类型,类等之所以存在的一个比较主要的理由。但信息隐藏并不只在使用抽象数据类型和类的时候才有意义。定义接口的时候,甚至实现的时候信息隐藏这一原则同样可以发挥作用。
比如说:实现方法A的时候,实际上只需要类的两个属性,但却把整个类定义成了这个方法的参数,这也是违背信息隐藏原则的。
还有一种比较有意思的情况是,目的是信息隐藏,但实际上却违背了信息隐藏原则。
比如说:类的方法中常常会使用到成员变量。有时候和类自身关联不是很紧密的属性也会被添加为成员变量。像表征数据库的类,比如DataBase,大多时候并不需要和获取数据的类,比如传感器的类Sensor直接关联,但有时候Sensor的实例却会被添加为DataBase的私有成员变量。
与此同时,大多时候类中的方法并不会把成员变量作为自身的参数,而是会直接使用,比如说:由于Sensor的实例是DataBase的私有成员变量,那么DataBase::GatherData()在用到Sensor实例的时候会直接使用,而不会把接口定义为.DataBase::GatherData(Sensor& sensor )。这种用法与成员变量过度膨胀的趋势撞在一起后,有意思的事情发生了。
从外部看,传感器这样的成员变量是私有的,达到了信息隐藏的目的。
但对于方法而言,结果却完全相反。
每多使用一个成员变量,其自身的信息隐藏就被破坏一点 。
如果在同一方法中,用到五六个成员变量,那么这个方法会变得非常难懂。因为对于类中的方法而言,这些成员变量自身的变化是完全不被隐藏的,每个都有有自己独立的脉络,比如说:DataBase::SetInterval()方法中可能会调整Sensro实例的状态,进而对Sensor::GatherData()产生影响。这会导致方法的边界不清晰。不清晰,也就不可能紧凑。不紧凑意味着阅读的时候,一个方法所需要的信息不能立刻获取。最终会导致程序难读,难改,难维护。
这类实例提醒我们,并非把很多操作和数据成员都放到类里面就达成了信息隐藏的目的。
事实上这对每个类的可能规模提出了限制。极端的情况下,如果一个类很大,有2000SLOC,那么所有这2000SLOC代码就等于通过成员变量粘结在一起,这和过度使用全局变量在一定程度上等价。
从信息隐藏的程度上来看,对方法而言,局部变量最佳,类的成员或属性稍差,全局变量最差。
局部变量把信息隐藏在方法之内,成员或属性把信息隐藏在类之内,而全局变量等于不做信息隐藏。
信息隐藏和前面提到的层次问题并不能分割开来。从基本趋势来看,信息隐藏做的越彻底,层次也必然就会越多。而前面曾经提及过层级自身是有副作用的。
从最小化层次的副作用的角度来看,使用函数或方法来隐藏数据,使用类似抽象数据类型的方法来把数据封装到类之中,代价非常微小,几乎总是对的。但在类A中包含B,类B中包含C,类C中包含D这类的方式则福祸难料。诚然B,C,D被隐藏了,但绝不是毫无代价。
--------------------------------------------------------------
理想流 + 软件 = 《完美软件开发:方法与逻辑》
理想流 + 人生 = ??
理想流 + 管理 = ??
理想流 = 以概念和逻辑推演本质,追求真理。