抽象数据类型(ADT)就是指用户在使用某个类的时候只能通过方法来操作这个类,而不能直接地访问这个类的属性。这样一来,程序的独立性和安全性都有了很大的提高。
实现抽象数据类型,我们可以把所有的属性都变成private,这种实现从工作量上来说比起public是增大很多的。但是如果用public可能会导致很多安全问题。构造一个ADT,我们需要写明ADT的特性:表示泄露,抽象函数AF,表示不变量RI,这些不是给用户看的,而是给程序员看的。ADT是由操作定义的,而与其内部实现无关。
一般来说,我们把操作方法分为四种,但是也只是人为区分,并不是编译器区分,1,构造器constructor。 2,生产器producer。3,观察器 observer。4,变值器mutators,用来改变对象的属性的方法。对于immutable类型的类,是没有第四个操作也就是变值器的,变值器只存在于mutable的类型当中。对于creator,为了避免将构造器暴露给用户,我们可以使用静态方法,称为工厂方法来返回一个当前类型的对象。
对于设计一个抽象数据类型,我们首先要做到的是设计简单,一致的操作。第二是要支持用户的对数据的所有操作,而且难度要低,例如list的get操作以及list的size操作。
对于抽象数据类型,它有表示独立性,也就是说,用户是不需要考虑ADT的内部实现的,因为这和ADT的使用无关,而且ADT的内部变化是不能影响到外部的规约以及客户端的。再者除非ADT指明了前提条件以及后验条件,否则不能改变ADT的内部表示。
然后是ADT的不变性,即ADT有自己的不变量,例如一些不可改变的数据,如果它是mutable的类型,那么在每次操作后都要检测这个不变量,保证它不能被改变。不变量是由ADT来负责的,与用户的所有操作都无关。即ADT来检测客户端是否恶意破坏ADT的不变性。
其次我们应该避免ADT的表示泄露,即避免public的属性,避免方法return的时候返回内部属性的引用,这都可能导致ADT的表示泄露。
除此之外,我们还要设计AF和RI,一般来说,我们可以先设计数据结构,也就是存储用户输入的数据。然后就应该设计AF,也就是将每个Rep如何映射到abstract value做出具体的解释。最后我们再来设计RI。最好的设计应该是一一对应的,因为这样设计起来使用最方便。而且我们应该把AF和RI写到程序的注释中,否则根本不知道方法是如何实现的。
另外,我们应该在程序运行的过程中随时检测Rep,即在所有可能改变Rep的操作中,observer虽然不会改变,一般来说不用检测,但是建议也要检测,以防万一。
对于ADT的规约,我们只能用到用户可见的内容来写,如参数,返回值或者异常,而不是内部的实现方法。显然,如果规约里有值,那么也只能用Abstract value中的值。
要检测ADT是否保持不变量,我们可以从三个大方面来看。首先是ADT
不存在表示泄露,也就是暴露内部属性或者其引用。然后是ADT的构造器和生产器的操作是合法的,保持不变量的。 最后是ADT的变值器和观察器的操作也合法,在执行时不改变RI。