不要把所有的全局数据都放入同一个模块中。如果你把所有的全局数据都归成一个大堆,
并编写对其存取的子程序,的确可以消除由全局数据带来的问题,但同时也抛掉了信息隐蔽和
抽象数据类型所带来的优点。编写存取子程序之前,应先考虑一下每一全局数据应属于哪一个
模块,然后把这个全局数据、相应的存取子程序和其关联的子程序放入那个模块中。
在存取子程序中建立某种程度的抽象。在数据所代表的意义层次上而不是计算机本身的实
现细节层次上建立存取子程序,可以使你更容易应付可能的变动。
请比较下列两组语句:
直接使用复杂数据通过存取子程序使用复杂数据
node=node.next node=nearestNeighbor(node)
node=node.next node=nextEmployee(node)
node=node.next node=nextRatele(node)
Event=EventQueue[QueueFront] Event=HighestprioirtyEvent( )
Event=EventQueue[QueueFront] Event=LowesPriotityEvent( )
表中前三个语句对中,抽象的存取子程序告诉你的信息要比数据结构所告诉你的多得多。
如果直接使用数据结构,那么一次需要做工作就太多了。你必须在表示出数据结构本身正在做
什么(移到链表中的下一个链)的同时,表示出正在对数据结构所代表的实体做什么(调用一
个邻居、下一个雇员或税率),这对于简单数据结构来说是很重的负担。把信息隐蔽在存取子程
序后面,可以使代码自己指出这些,并且可以使得其它人从问题域而不是实现细节的层次上来
阅读程序。
把对数据的所有存取保持在同一抽象水平上。如果你用了某一存取子程序对某一数据进行
了一项操作,那么对这一数据的其它操作也应通过存取子程序来实现。比如若是通过存取子程
序来从数据结构中读取数值的,那么对该数据结构的写操作也应通过存取子程序来实现。又比
如假设你通过调用initstack()子程序将元素压入堆栈,但你接着又用value=array(strack.top)来
获得堆栈的一个入口,那么此时对数据的观察点便不连续了。这种不连续性使别人很难理解你
的程序。因此,要保持对数据所有存取操作抽象水平的一致性。
在上表中的后两个语句对中,两个事件排队的操作是平行进行的。在队列中插入一个事件
将比表中其它操作都更复杂,因为你不得不改变队列的前后顺序,调整现存事件以便为它腾出
空间,再写几行代码以便找到插入它的地方等等,从一个序列中移出一个事件几乎是同样麻烦
的。因此,在编码时如果把复杂操作放入子程序,而其余操作直接对数据进行,将产生对数据
结构拙劣的、非平等的使用。请比较下面的两组语句:
对复杂数据的非平行使用对复杂数据的平行使用
对复杂数据的非平行使用对复杂数据的平行使用
Event=EventQueue[QueueFront] Event=HightestPriorityEvent( )
Event=EventQueue[QueueBack] Event=LowestPriorityEvent( )
AddEvent(Event) AddEvent(Event)
EventCount=EventCount-1 RemoveEvent(Event)
应注意这些准则适用许多模块和子程序构成的大型程序。在小一些的程序中,存取子程序