荟萃与担任器
针对一个特定问题的办理,假如事先不知道需要几多个工具,可能它们的一连时间有多长,那么也不知道如何生存那些工具。既然如此,奈何才气知道那些工具要求几多空间呢?事先上基础无法提前知道,除非进入运行期。
在面向工具的设计中,大大都问题的办理步伐好像都有些轻率——只是简朴地建设另一种范例的工具。用于办理特定问题的新型工具容纳了指向其他工具的句柄。虽然,也可以用数组来做同样的工作,那是大大都语言都具有的一种成果。但不能只看到这一点。这种新工具凡是叫作“荟萃”(亦叫作一个“容器”,但AWT在差异的场所应用了这个术语,所以本书将一直沿用“荟萃”的称号。在需要的时候,荟萃会自动扩充本身,以便适应我们在个中置入的任何对象。所以我们事先不必知道要在一个荟萃里容下几多对象。只需建设一个荟萃,今后的事情让它本身认真好了。
幸运的是,设计优良的OOP语言都配套提供了一系列荟萃。在C++中,它们是以“尺度模板库”(STL)的形式提供的。Object Pascal用本身的“可视组件库”(VCL)提供荟萃。Smalltalk提供了一套很是完整的荟萃。而Java也用本身的尺度库提供了荟萃。在某些库中,一个通例荟萃便可满意人们的大大都要求;而在另一些库中(出格是C++的库),则面向差异的需求提供了差异范例的荟萃。譬喻,可以用一个矢量统一对所有元素的会见方法;一个链接列表则用于担保所有元素的插入统一。所以我们能按照本身的需要选择适当的范例。个中包罗集、行列、散列表、树、仓库等等。
所有荟萃都提供了相应的读写成果。将某样对象置入集适时,回收的方法是十理解显的。有一个叫作“推”(Push)、“添加”(Add)或其他雷同名字的函数用于做这件工作。但将数据从荟萃中取出的时候,方法却并不老是那么明明。假如是一个数组形式的实体,好比一个矢量(Vector),那么也许能用索引运算符或函数。但在很多环境下,这样做往往会无功而返。另外,单选定函数的成果长短常有限的。假如想对荟萃中的一系列元素举办哄骗或较量,而不是仅仅面向一个,这时又该怎么办呢?
步伐就是利用一个“继承器”(Iterator),它属于一种工具,认真选择荟萃内的元素,并把它们提供应担任器的用户。作为一个类,它也提供了一级抽象。操作这一级抽象,可将荟萃细节与用于会见谁人荟萃的代码隔分开。通过担任器的浸染,荟萃被抽象成一个简朴的序列。担任器答允我们遍历谁人序列,同时毋需体贴基本布局是什么——换言之,不管它是一个矢量、一个链接列表、一个仓库,照旧其他什么对象。这样一来,我们就可以机动地改变基本数据,不会对措施里的代码造成滋扰。Java最开始(在1.0和1.1版中)提供的是一个尺度担任器,名为Enumeration(列举),为它的所有荟萃类提供处事。Java 1.2新增一个更巨大的荟萃库,个中包括了一个名为Iterator的担任器,可以做比老式的Enumeration更多的工作。
从设计角度出发,我们需要的是一个全成果的序列。通过对它的哄骗,应该能办理本身的问题。假如一种范例的序列即可满意我们的所有要求,那么完全没有须要再换用差异的范例。有两方面的原因促使我们需要对集相助出选择。首先,荟萃提供了差异的接口范例以及外部行为。仓库的接口与行为与行列的差异,而行列的接口与行为又与一个集(Set)或列表的差异。操作这个特征,我们办理问题时便有更大的机动性。
其次,差异的荟萃在举办特定操纵时往往有差异的效率。最好的例子即是矢量(Vector)和列表(List)的区别。它们都属于简朴的序列,拥有完全一致的接口和外部行为。但在执行一些特定的任务时,需要的开销却是完全差异的。对矢量内的元素举办的随时机见(存取)是一种常时操纵;无论我们选择的选择是什么,需要的时间量都是沟通的。但在一个链接列表中,若想处处移动,并随机挑选一个元素,就需支付“惨重”的价钱。并且假设某个元素位于列表较远的处所,找到它所需的时间也会长很多。但在另一方面,假如想在序列中部插入一个元素,用列表就比用矢量划算得多。这些以及其他操纵都有差异的执行效率,详细取决于序列的基本布局是什么。在设计阶段,我们可以先从一个列表开始。最后调解机能的时候,再按照环境把它换成矢量。由于抽象是通过担任器举办的,所以能在两者利便地切换,对代码的影响则显得微不敷道。
最后,记着荟萃只是一个用来安排工具的蕴藏所。假如谁人蕴藏所能满意我们的所有需要,就完全没须要体贴它详细是如何实现的(这是大大都范例工具的一个根基观念)。假如在一个编程情况中事情,它由于其他因素(好比在Windows下运行,可能由垃圾收集器带来了开销)发生了内涵的开销,那么矢量和链接列表之间在系统开销上的差别就或者不是一个大问题。我们大概只需要一种范例的序列。甚至可以想象有一个“完美”的荟萃抽象,它能按照本身的利用方法自动改变下层的实现方法。