面向對象軟件構造(第2版)-第6章 Abstract data types抽象數據類型 (上)

This opened my mind, I started to grasp what it means to use the tool known as algebra. I’ll be damned if anyone had ever told me before: over and again Mr. Dupuy [the mathematics teacher] was making pompous sentences on the subject, but not once would he say this simple word: it is a division of labor, which like any division of labor produces miracles, and allows the mind to concentrate all of its forces on just one side of objects, on just one of their qualities.

這啓了我的思維,我開始領會使用代數工具的意圖。在此之前,可沒有任何人告訴過我: Dupuy先生[數學老師]在主題上不斷地誇誇其談,但是他一次也沒說這個簡單的詞組:這是勞動力的分工(division of labor),就象創造奇蹟的任何勞動分工一樣,允許把所有的思維集中在對象的某個方面上,在其中的一個品質上。

 

What a difference it would have made for us if Mr. Dupuy had told us: This cheese is soft or it is hard; it is white, it is blue; it is old, it is young; it is yours, it is mine, it is light or it is heavy. Of so many qualities let us consider only the weight. Whatever that weight may be, let us call it A. Now, without thinking of the weight any more, let us apply to A everything that we know of quantities.

假如Dupuy先生告訴我們其中的不同,這就會對我們有所幫助了: 這奶酪很軟或很硬;它是白色的,它是藍色的;它是熟的,它是生的;它是您的, 它是我的,它是輕的或是重的。在這麼多的品質中讓我們只考慮重量。無論其重量是多少,讓我們稱之爲A。現在,不再考慮其重量,讓我們把所有我們知其量的每件事物都應用到A上。

 

Such a simple thing; yet no one was saying it to us in that faraway province¼

一件如此簡單的事物;在那個偏僻的省份中,一直無人能告訴我們

 

Stendhal, The Life of Henry Brulard, 1836.

 

For abstraction consists only in separating the perceptible qualities of bodies, either from other qualities, or from the bodies to which they apply. Errors arise when this separation is poorly done or wrongly applied: poorly done in philosophical questions, and wrongly applied in physical and mathematical questions. An almost sure way to err in philosophy is to fail to simplify enough the objects under study; and an infallible way to obtain defective results in physics and mathematics is to view the objects as less composite than they are.

因爲抽象只存在於分離的可感知的主體本質中,即從其它的本質中來,也從它們應用的主體中來。錯誤源自於當分離無法完成或應用失誤的時候: 無法完成發生在哲學的問題中,應用失誤出現在物理和數學的問題上。一個幾乎肯定會在哲學中引發錯誤的方法是沒能把研究的對象足夠簡單化;在物理和數學上,一個確定能獲得有缺陷的結果的途徑是不能把對象分解到底。

 

Denis Diderot, A Letter on the Blind for the Benefit of Those Who Can See, 1749.


Letting objects play the lead role in our software architectures requires that we describe them adequately. This chapter shows how.

要讓對象在我們的軟件架構中起着領導作用,這需要我們充分地描述對象。在本章中將展現如何描述它們。

 

You are perhaps impatient to dive into the depths of object technology and explore the details of multiple inheritance, dynamic binding and other joys; then you may at first look at this chapter as an undue delay since it is mostly devoted to the study of some mathematical concepts (although all the mathematics involved is elementary).

您也許迫不及待地想鑽研對象技術並想研究多重繼承,動態綁定和其它感興趣技術的細節;由於本章大部分專注於一些數學概念上的研究(雖然這裏所用的數學概念都是最基本的),於是,剛開始您可能會認爲本章沒甚麼必要。

 

But in the same way that even the most gifted musician will benefit from learning a little music theory, knowing about abstract data types will help you understand and enjoy the practice of object-oriented analysis, design and programming, however attractive the concepts might already appear without the help of the theory. Since abstract data types establish the theoretical basis for the entire method, the consequences of the ideas introduced in this chapter will be felt throughout the rest of this book.

但是同樣地,即使是最富天才的音樂家也會從學習一段簡短的音樂理論中受益, 瞭解有關抽象數據類型將會幫助您理解和喜愛面向對象的分析,設計和編程的實踐,然而在沒有理論的幫助下,引人入勝的概念就已經出現了。由於抽象數據類型對於整個方法建立了理論基礎,所以本章所介紹的概念結論將會貫穿於本書。

 

There is more. As we will see at chapter end, these consequences actually extend beyond the study of software proper, yielding a few principles of intellectual investigation which one may perhaps apply to other disciplines.

不僅僅是這些。在本章結束時我們就能看到,這些結論實際上被完全地擴充了,超越了嚴格意義上的軟件研究,併產生了一些可以適用於其它學科的理性研究的原則。

 

6.1 CRITERIA

6.1 標準

 

To obtain proper descriptions of objects, we need a method satisfying three conditions:

要獲得對象的正確描述,我們需要一個方法能滿足下列三個條件:

 

• The descriptions should be precise and unambiguous.

·描述應該是精確和無歧義的。

 

• They should be complete — or at least as complete as we want them in each case (we may decide to leave some details out).

·描述應該是完整的-或至少在每種情況裏面和我們所希望的一樣完整(我們可以選擇忽略一些細節)。

 

• They should not be overspecifying.

·描述不應該冗餘(overspecifying)

 

The last point is what makes the answer non-trivial. It is after all easy to be precise, unambiguous and complete if we “spill the beans” by giving out all the details of the objects’ representation. But this is usually too much information for the authors of software elements that need to access the objects.

最後一條是說讓答案切中要害。如果我們“無意中”發佈了對象表示法的所有細節,那麼最終是能輕易地得到了精確的,不含糊的和完整的描述。但是這通常對於那些需要使用對象的軟件元素的作者來說信息過多了。

 

This observation is close to the comments that led to the notion of information hiding. The concern there was that by providing a module’s source code (or, more generally, implementation-related elements) as the primary source of information for the authors of software elements that rely on that module, we may drown them in a flood of details, prevent them from concentrating on their own job, and hamper prospects of smooth evolution. Here the danger is the same if we let modules use a certain data structure on the basis of information that pertains to the structure’s representation rather than to its essential properties.

這個觀點與信息隱藏觀念的結論比較接近。對於依賴那個模塊的軟件元素的作者來說,考慮到如果通過提供給他們這個模塊的源代碼(或者,更通常的是提供相關實現元素的源碼)作爲信息的主要來源,我們可能會把他們淹沒在細枝末節的汪洋大海中,妨礙他們集中於自己的工作,而且阻礙了平滑演化的可能性。在這裏,如果我們讓模塊使用一個特定的數據結構,而這個數據結構是以符合結構表示法的信息爲基礎,而不是它的基本屬性的話,那麼其危險性是同樣的。

 

6.2 IMPLEMENTATION VARIATIONS

6.2 實現變體

 

To understand better why the need for abstract data descriptions is so crucial, let us explore further the potential consequences of using physical representation as the basis for describing objects.

要更好地瞭解爲什麼對抽象數據描述的需求是如此決定性的, 我們就要進一步研究使用物理示法作爲描述對象基礎的可能結果。

 

A well-known and convenient example is the description of stack objects. A stack object serves to pile up and retrieve other objects in a last-in, first-out (“LIFO”) manner, the latest inserted element being the first one to be retrieved. The stack is a ubiquitous structure in computing science and in many software systems; the typical compiler or interpreter, for example, is peppered with stacks of many kinds.

一個衆所周知的,適宜的例子是棧對象的描述。在後進先出的方式中("LIFO"),一個棧對象用於存放和取回其它的對象,最後插進的元素被最先取回。在計算科學方面和在衆多軟件的系統中,棧是一個無處不在的結構;例如,典型的編譯器或解釋器大量使用多種類型的棧。

 

Stacks, it must be said, are also ubiquitous in didactic presentations of abstract data types, so much so that Edsger Dijkstra is said to have once quipped that “abstract data types are a remarkable theory, whose purpose is to describe stacks”. Fair enough. But the notion of abstract data type applies to so many more advanced cases in the rest of this book that I do not feel ashamed of starting with this staple example. It is the simplest I know which includes about every important idea about abstract data types.

可以這麼說,棧也到處存在於抽象數據類型的教學活動中,如此之多以至於據說Edsger Dijkstra曾經嘲諷“抽象數據類型是一個卓越的理論,其目的就是描述棧”。此言不假。但是抽象數據類型的概念應用到本書如此衆多的高級範例中,以至於我並不認爲以這個常用的例子開始有什麼不好意思。這是我所知的最簡單的例子,它包含了抽象數據類型的每一個重要的思想。

 

Stack representations

表示法

 

Several possible physical representations exist for stacks:

存在幾種可能的物理表示法:

 

 


The figure illustrates three of the most common representations. Each has been given a name for ease of reference:

圖例描繪了三種最常用的表示法。每一種都有一個便於引用的名字:

 

ARRAY_UP: represent a stack through an array representation and an integer count whose value ranges from 0 (for an empty stack) to capacity, the size of the array representation; stack elements are stored in the array at indices 1 up to count.

·ARRAY_UP:由一個數組representation和一個整數count來描述的一種整數值的範圍從0(一個空棧)到capacitycapacity是數組representation的大小;棧元素被存儲在索引爲1count數組中。

 

ARRAY_DOWN: like ARRAY_UP, but with elements stored from the end of the array rather than from the beginning. Here the integer is called free (it is the index of the highest free array position, or 0 if all positions are occupied) and ranges from capacity for an empty stack down to 0. The stack elements are stored in the array at indices capacity down to free + 1.

·ARRAY_DOWN:如同ARRAY_UP,但其元素從數組的末端開始存儲,而不是從起始位置。這裏的整數被稱之爲free(其值是最大的空數組位置的索引,如果所有位置都滿了則爲0),範圍從空棧位置capacity減至0棧元素被存儲在索引爲capacity下至free + 1數組中。

 

LINKED: a linked representation which stores each stack element in a cell with two fields: item representing the element, and previous containing a pointer to the cell containing the previously pushed element. The representation also needs last, a pointer to the cell representing the top.

·LINKED:一種鏈接表示法,在其單元內用兩個字段存儲每一個元素:item表示元素,previous包含一個指針指向前一個元素所在的單元。這種表示法也需要last,一個指向首端的指針。

 

Next to each representation, the figure shows a program extract (in Pascal-like notation) giving the corresponding implementation for a basic stack operation: pushing an element x onto the top.

在每種表示法的旁邊,圖例都顯示了一個程序片段(類Pascal語言符號),爲一個基本的棧操作提供了對應的實現: 把元素x壓入棧頂。

 

For the array representations, ARRAY_UP and ARRAY_DOWN, the instructions increase or decrease the top indicator (count or free) and assign x to the corresponding array element. Since these representations support stacks of at most capacity elements, robust implementations should include guards of the respective forms

if count < capacity then ¼

if free > 0 then ¼

which the figure omits for simplicity.

對於數組表示法ARRAY_UPARRAY_DOWN指令增加或減少棧頂指示符(countfree)x賦值到對應的數組元素中。由於這些表示法支持最多capacity個元素的棧因此健壯的實現應該包括各自的保護措施

if count < capacity then ¼

if free > 0 then ¼

爲了簡單,圖例中省略了這些條件。

 

For LINKED, the linked representation, pushing an element requires four operations: create a new cell n (done here with Pascal’s new procedure, which allocates space for a new object); assign x to the new cell’s item field; chain the new cell to the earlier stack top by assigning to its previous field the current value of last; and update last so that it will now be attached to the newly created cell.

對於LINKED鏈接表示法,壓入一個元素進棧需要四步操作:創建一個新的存儲單元n(這裏是由Pascalnew過程完成的,它給一個新的對象分派空間);把x賦值到新存儲單元nitem字段中;通過把last指向的當前單元賦值到nprevious字段,鏈接新的單元到棧的頂端;最後更新last以便它現在指向新創建的存儲單元n

 

Although these are the most frequently used stack representations, many others exist. For example if you need two stacks of elements of the same type, and have only limited space available, you may rely on a single array with two integer top markers, count as in ARRAY_UP and free as in ARRAY_DOWN; one of the stacks will grow up and the other will grow down. The representation is full if and only if count = free.

這些都是最常使用的棧表示法,還有許多其它的方法。舉例來說,如果您需要二個具有相同類型的元素的棧,並且只有有限的存儲空間,那麼您可以使用一個單一數組,它帶有兩個整數類型的棧頂標記,ARRAY_UP中的countARRAY_DOWN中的free;其中一個向上增長,另一個向下遞減。當count = free時表示棧滿了。

 


The advantage, of course, is to lessen the risk of running out of space: with two arrays of capacity n representing stacks under ARRAY_UP or ARRAY_DOWN, you exhaust the available space whenever either stack reaches n elements; with a single array of size 2n holding two head-to-head stacks, you run out when the combined size reaches 2n, a less likely occurrence if the two stacks grow independently. (For any variable values p and q, max (p + q) £ max (p) + max (q).)

當然,這有減少耗盡空間危險的好處:如果ARRAY_UPARRAY_DOWN各用容量爲n的數組來表示棧,只要其中一個達到了n元素您就用盡了棧的有效空間;如果由一個大小爲2n的單一數組容納二個相對而行的棧,當合起來的大小到達2n時,您才用完空間,二個棧獨立地增長的情況倒也不常發生。(對於任意的有效變量pqmax (p + q) £ max (p) + max (q))

 

Each of these and other possible representations is useful in some cases. Choosing one of them as “the” definition of stacks would be a typical case of overspecification. Why should we consider ARRAY_UP, for example, more representative than LINKED? The most visible properties of ARRAY_UP — the array, the integer count, the upper bound — are irrelevant to an understanding of the underlying structure.

上述的每一種和其它可能的表示法都適用於一定的情況。選擇其中的一個作爲棧的定義可能會是一個規格冗餘的典型情況。舉例來說,我們爲什麼應該考慮更具代表性的ARRAY_UP,而不是LINKED? ARRAY_UP中最主要的屬性-數組,整數count,上界-並不牽涉到對於底層結構的理解。

 

The danger of overspecification

規格冗餘的危險

 

Why is it so bad to use a particular representation as specification?

爲什麼採用一個特別的表示法作爲規格是相當糟糕的?

 

The results of the Lientz and Swanson maintenance study, which you may recall, give a hint. More than 17% of software costs was found to come from the need to take into account changes of data formats. As was noted in the discussion, too many programs are closely tied to the physical structure of the data they manipulate. A method relying on the physical representation of data structures to guide analysis and design would not be likely to yield flexible software.

讓我們回想一下,LientzSwanson有關維護的研究結果給出了一個提示。研究發現超過17%的軟件費用用於考慮數據格式變化的要求。討論中已經提到了,太多的程序被綁定在它們所操縱的物理數據結構上。一個依賴數據結構的物理表示法來引導分析和設計的方法,將不可能產生具有靈活性的軟件。

 

So if we are to use objects or object types as the basis of our system architectures, we should find a better description criterion than the physical representation.

所以,如果我們要用對象或對象類型作爲我們系統架構的基礎,我們應該找出一個比物理表示法更好的描述標準。

 

How long is a middle initial?

一箇中間名(middle initial)多長?

 

Lest stacks make us forget that, beyond the examples favored by computer scientists, data structures are ultimately connected with real-life objects, here is an amusing example, taken from a posting on the Risks forum (comp.risks Usenet newsgroup) of the dangers of a view of data that is too closely dependent on concrete properties:

要避免棧會使我們忘記數據結構纔是最終連接到現實的對象上,除了計算機科學家們所偏愛的例子外,這裏還有一個有趣的例子,摘自於一個風險論壇上(comp.risks Usenet新聞組)的帖子,此帖的一個數據危害性的觀點是太過於依賴實際的屬性:

 

My dear mother blessed (or perhaps cursed) all of her children with two middle initials, in my case “D” and “E”. This has caused me a good deal of trouble.

我親愛的母親用兩個中間名祝福(也許是詛咒)她所有的孩子,我的情況是DE。這給我造成了很多的麻煩。

 

It seems that TRW sells certain parts of your credit information, such as your name and a demographic profile. I recently got a new credit card from Gottchalks and found to my chagrin that my name had been truncated to “Darrell D. Long”. I went to the credit manager and was assured that things would be fixed. Well, two things happened: I got a new credit card, this time as “Darrell E. Long”, and TRW now has an annotation in my file to the effect “File variation: middle initial is E”. Soon after this I start getting mail for “Darrell E. Long” (along with the usual “Darrell Long” and “Darrell D. Long” and the occasional “Darrell D. E. Long”).

似乎TRW泄露了您的信用信息的某些部份,像是您的名字和個人的概況。我最近從Gottchalks得到一張新的信用卡,而使我懊惱的是我發現我的名字已經被切斷成" Darrell D. Long"。我去找信用卡部的經理並確保將會被修改好。哦,第二件事發生了: 我得到了一張新的信用卡,這次是"Darrell E. Long",並且現在TRW在我的檔案中的註解是"文檔改變: 中間名是E"。不久,我開始收到發給"Darrell E. Long"的郵件(隨着平時所用的"Darrell Long""Darrell D. Long",偶爾還有"Darrell D. E. Long")

 

I called up the credit bureau and it seems that the programmer who coded up the TRW database decided that all good Americans are entitled to only one middle initial. As the woman on the phone patiently told me “They only allocated enough megabytes (sic) in the system for one middle initial, and it would probably be awfully hard to change”.

我打電話給信用部門,似乎在TRW數據庫上面編碼的程序員決定了所有良好的美國市民只能有一箇中間名。正如在電話的另一端上的婦女耐心地告訴我"在系統中只爲一箇中間名分配了足夠的兆字節(MB)(原文如此),這恐怕會很難更改"

 

Aside from the typical example of technobabble justification (“megabytes”), the lesson here is the need to avoid tying software to the exact physical properties of data. TRW’s system seems similar to those programs, mentioned in an earlier discussion, which “knew” that postal codes consist of exactly five digits.

除了這個可笑的技術理由(兆字節)的典型例子之外,這裏的教訓是需要避免使軟件依賴於數據的精確物理屬性。TRW的系統看上去似乎很像在之前討論中提到的那些程序,"已知"郵政編碼包含了正好五位數字。

 

The author of the message reproduced above was mainly concerned about junk mail, an unpleasant but not life-threatening event; the archives of the Risks forum are full of computer-originated name confusions with more serious consequences. The “millenium problem”, mentioned in the discussion of software maintenance, is another example of the dangers of accessing data based on physical representation, this one with hundreds of millions of dollars’ worth of consequences.

上述編寫郵件程序的作者主要考慮的是垃圾郵件,一個討厭但不是威脅生命的事情;風險論壇的文檔中充滿了有着更嚴重後果的計算機造成的命名混亂。在軟件維護的討論中提到的"千年蟲問題",是另一個在物理表示法上存取數據的危險例子,其後果的價值數以百萬計。

 

6.3 TOWARDS AN ABSTRACT VIEW OF OBJECTS

6.3 有關對象的抽象觀點

 

How do we retain completeness, precision and non-ambiguity without paying the price of overspecification?

在不付出規格冗餘代價的前提下我們如何保持完整性(completeness)精確性(precision)和單值性(non-ambiguity)

 

Using the operations

使用運算

 

In the stack example, what unites the various representations in spite of all their differences is that they describe a “container” structure (a structure used to contain other objects), where certain operations are applicable and enjoy certain properties. By focusing not on a particular choice of representation but on these operations and properties, we may be able to obtain an abstract yet useful characterization of the notion of stack.

在棧的例子中,忽略其中差異而結合各種不同的表示法,就是它們所描述的“容器”結構(一個結構用於包含其它的對象),在其中可運用特定的運算並使用特定的屬性。通過集中在這些運算和屬性上而不是在表示法的特別選擇上,我們能夠獲得一個有關棧概念的抽象而有效的特徵描述。

 

The operations typically available on a stack are the following:

典型的棧運算如下:

 

• A command to push an element on top of a stack. Let us call that operation put.

·把一個元素壓入棧頂的命令。我們稱之爲put運算。

 

• A command to remove the stack’s top element, if the stack is not empty. Let us call it remove.

·在非空的情況下,一個移去棧頂元素的命令。我們稱之爲remove

 

• A query to find out what the top element is, if the stack is not empty. Let us call it item.

·在非空的情況下,一個找出棧頂元素的查詢。我們稱之爲item

 

• A query to determine whether the stack is empty. (This will enable clients to determine beforehand if they can use remove and item.)

·一個檢查棧是否爲空的查詢。(這使客戶預先決定是否能夠用removeitem)

 

In addition we may need a creator operation giving us a stack, initially empty. Let us call it make.

另外,我們需要一個創建符運算,使我們得到一個初始爲空的棧。我們稱之爲make

 

Two points may have caught your attention and will deserve more explanation later in this chapter. First, the operation names may seem surprising; for the moment, just think of put as meaning push, remove as meaning pop, and item as meaning top. Details shortly (on the facing page, actually). Second, the operations have been divided into three categories: creators, which yield objects; queries, which return information about objects; and commands, which can modify objects. This classification will also require some more comments.

有兩點要值得注意,本章稍後會進一步闡述。第一,運算命名也許有些出乎意料;目前,僅僅把put當成是pushremove當成是popitem當成是top。很快會公佈細節(其實就在下一頁)。第二,運算會被分成三類:創建符,其創建對象;查詢,其返回對象信息;命令,其維護對象。這種分類也將要更進一步地闡述。

 

In a traditional view of data structures, we would consider that the notion of stack is given by some data declaration corresponding to one of the above representations, for example (representation ARRAY_UP, Pascal-like syntax):

count: INTEGER

representation: array [1 l l capacity] of STACK_ELEMENT_TYPE

where capacity, a constant integer, is the maximum number of elements on the stack. Then put, remove, item, empty and make would be routines (subprograms) that work on the object structures defined by these declarations.

在傳統的數據結構的觀點中,我們認爲,符合上述某個表示法的一些數據聲明給定了棧的概念,例如(ARRAY_UP表示法,類Pascal符號)

count: INTEGER

representation: array [1 l l capacity] of STACK_ELEMENT_TYPE

其中,整數常量capacity表示棧中元素的最大個數。put, remove, item, emptymake是例程(子程序),它們在被這些聲明所定義的對象結構中運行。

 

The key step towards data abstraction is to reverse the viewpoint: forget for the moment about the representation; take the operations themselves as defining the data structure. In other words, a stack is any structure to which clients may apply the operations listed above.

理解數據抽象的主要關鍵是要顛覆上述之觀點: 暫時忘記表示法;把這些運算本身當作一個定義中的數據結構。換句話說,一個棧任意的結構,客戶端可以在其中應用上述所列出的運算。

 

A laissez-faire policy for the society of modules

對於模塊組織的經濟自由政策

 

The method just outlined for describing data structures shows a rather selfish approach to the world of data structures: like an economist of the most passionate supply-side, invisible-hand, let-the-free-market-decide school, we are interested in individual agents not so much for what they are internally as for what they have to offer to each other. The world of objects (and hence of software architecture) will be a world of interacting agents, communicating on the basis of precisely defined protocols.

對於瞭解數據結構領域來說,一個僅僅只是簡略描述數據結構輪廓的方法顯示了一種相當狹隘的方式:就像一位對於供應經濟學,“看不見的手”的規律,自由市場決定之類的教育政策極端狂熱的經濟學家, 對其個人動機,我們感興趣的是它們所能彼此之間提供的聯繫,而不是它們的內在作用。對象的領域(和此後的軟件架構)將成爲一個交互媒介的領域,根據精確地詳細定義的協議進行溝通。

 

The economic analogy will indeed accompany us throughout this presentation; the agents — the software modules — are called suppliers and clients; the protocols will be called contracts, and much of object-oriented design is indeed Design by Contract, the title of a later chapter.

經濟上的類比將會真正地伴隨着我們貫穿於整個陳述媒介軟件模塊被稱爲供應者(suppliers)客戶端(clients)協議稱之爲契約(contracts)而且大部分面向對象設計是真正的契約式設計(Design by Contract),這是後續章節的主題。

 

As always with analogies, we should not get too carried away: this work is not a textbook on economics, and contains no hint of its author’s views in that field. It will suffice for the moment to note the remarkable analogies of the abstract data type approach to some theories of how human agents should work together. Later in this chapter we will again explore what abstract data types can tell us beyond their original area of application.

由於總是使用類比,我們不應該過於陷入其中: 本書不是經濟學上的一本教科書,也並沒有包含作者對此領域所持的觀點。目前,這些類比足夠表明抽象數據類型方法與人類媒體協同工作的方法之間顯著的類似之處。在本章的後面,我們會再次研究抽象數據類型在其最初的應用領域之外所能告訴我們的東西。

 

Name consistency

命名一致性

 

For the moment, let us get back to more immediate concerns, and make sure you are comfortable with the above example specification in all its details. If you have encountered stacks before, the operation names chosen for the discussion of stacks may have surprised or even shocked you. Any self-respecting computer scientist will know stack operations under other names:

暫時,讓我們回到更迫切需要關心的問題上來,同時,確信您對上述例子的規格中所有的細節都沒有疑問。如果您以前遇到過棧,那麼對所討論中的棧的運算命名之選擇可能已經使您喫驚甚至感到震驚。任何自信的計算機科學家都會了解下列棧運算的其它命名:


 

Why use anything else than the traditional terminology? The reason is a desire to take a high-level view of data structures — especially “containers”, those data structures used to keep objects.

爲什麼要使用有別於傳統的術語? 原因是一個更高層次上的研究數據結構的要求-特別是"容器",即那些用來保存對象的數據結構。

 

Stacks are just one brand of container; more precisely, they belong to a category of containers which we may call dispensers. A dispenser provides its clients with a mechanism for storing (put), retrieving (item) and removing (remove) objects, but without giving them any control over the choice of object to be stored, retrieved or removed. For example, the LIFO policy of stacks implies that you may only retrieve or remove the element that was stored last. Another brand of dispenser is the queue, which has a first-in, first-out (FIFO) policy: you store at one end, retrieve and remove at the other; the element that you retrieve or remove is the oldest one stored but not yet removed. An example of a container which is not a dispenser is an array, where you choose, through integer indices, the positions where you store and retrieve objects.

棧只是容器的類型之一更精確地說是屬於一種我們稱之爲分配器(dispensers)的容器。一個分配器提供了一個機制給它的客戶端來存儲(put), 獲取(item)和刪除(remove)對象,但是對於被存儲,獲取或刪除的對象之選擇,並沒有給其客戶端任何地控制。舉例來說,棧的LIFO政策意味着您只可能獲取或刪除在最後一個被儲存的元素。另一種分配器是隊列,它採取的是先進先出(FIFO)策略:您在一端存儲,在另一端獲取和刪除;您獲取或是刪除的元素是沒被刪除中的最早存儲的一個。一個不是分配器的容器例子是數組,在其中通過整數索引選擇您儲存和獲取對象的位置。

 

Because the similarities between various kinds of container (dispensers, arrays and others) are more important than the differences between their individual storage, retrieval and removal properties, this book constantly adheres to a standardized terminology which downplays the differences between data structure variants and instead emphasizes the commonality. So the basic operation to retrieve an element will always be called item, the basic operation to remove an element will always be called remove and so on.

因爲,在各種不同類型的容器(分配器,數組等等)之間的相似性比在它們單獨地存儲,獲取和刪除屬性之間的差異更爲重要,所以本書一直堅持一個標準化的術語,來忽略數據結構變體之間的差異,取而代之的是強調共通性。因此獲取一個元素的基本操作將總是被稱爲item,刪除一個元素的基本操作總是被稱之爲remove等等。

 

These naming issues may appear superficial at first — “cosmetic”, as programmers sometimes say. But do not forget that one of our eventual aims is to provide the basis for powerful, professional libraries of reusable software components. Such libraries will contain tens of thousands of available operations. Without a systematic and clear nomenclature, both the developers and the users of these libraries would quickly be swamped in a flood of specific and incompatible names, providing a strong (and unjustifiable) obstacle to large-scale reuse.

正如程序員有時說的那樣,這些命名的議題起先可能顯得有些無關緊要-"修飾物"。但是不要忘記我們的最終目標之一是對強大的,專業的可複用軟件組件庫提供基礎。這樣的庫將會包含成千上萬的有效運算。沒有一個系統的和清晰的命名法則,這些庫的開發者和用戶很快地會陷入在這些特殊和矛盾的命名之中,這對大範圍的複用提供了巨大(和不合理的)的障礙。

 

Naming, then, is not cosmetic. Good reusable software is software that provides the right functionality and provides it under the right names.

因而,命名並是修飾物。良好的可複用軟件是能提供準確的功能性和正確的命名的軟件。

 

The names used here for stack operations are part of a systematic set of naming conventions used throughout this book. A later chapter will introduce them in more detail.

這裏所使用的棧運算的命名,是貫穿於本書的系統性命名約定的一部份。更詳細地介紹請見後。

 

How not to handle abstractions

如何忽略抽象性

 

In software engineering as in other scientific and technical disciplines, a seminal idea may seem obvious once you have been exposed to it, even though it may have taken a long time to emerge. The bad ideas and the complicated ones (they are often the same) often appear first; it takes time for the simple and the elegant to take over.

就象在其它的科學技術學科中一樣,在軟件工程中一旦您已經受到一個創造性概念的影響,就可能認爲這個概念是顯而易見的,即使它可能曾經花了很長時間才脫穎而出。差勁和複雜難懂的概念(它們常常是難兄難弟)通常倒是一開始就冒出頭來;而簡單和優雅的理論去要花上一段時間才能被接受。

 

This observation is true of abstract data types. Although good software developers have always (as a result of education or mere instinct) made good use of abstraction, many of the systems in existence today were designed without much consideration of this goal.

這個結論對抽象數據類型也適用。雖然優秀的軟件開發者總是(作爲教育的結果或者僅僅是出於本能)很好地利用抽象, 但是現存的許多系統沒有過多考慮這一個目標就被設計出來了。

 

I once did a little involuntary experiment which provided a good illustration of this state of affairs. While setting up the project part of a course which I was teaching, I decided to provide students with a sort of anonymous marketplace, where they could place mock “for sale” announcements of software modules, without saying who was the source of the advertisement. (The idea, which may or may not have been a good one, was to favor a selection process based only on a precise specification of the modules’ advertized facilities.) The mail facility of a famous operating system commonly favored by universities seemed to provide the right base mechanism (why write a new mail system just for a course project?); but naturally that mail facility shows the sender’s name when it delivers a message to its recipients. I had access to the source of the corresponding code — a huge C program — and decided, perhaps foolishly, to take that code, remove all references to the sender’s name in delivered messages, and recompile.

我曾經做過一個偶然的小實驗,對這種事態提供了一個很好的佐證。作爲我正在講授的課程中的一部分,我設立了一個項目,決定提供給學生一種(虛擬)匿名的市場,在那裏他們可以放置一些模擬的“待售”軟件模塊的公告,並不說明廣告的作者來源。(不管好不好,這個主意只是在一個模塊的廣告工具的精確規格上做一次選擇過程罷了。)一個在大學普遍使用的著名的操作系統中的郵件工具似乎能夠提供合意的基本機制(爲什麼僅僅針對一個課程項目去寫一個新的郵件系統?);但是當它發送一個消息給收件人的時候,郵件工具很自然地顯示了寄件人的名字。我查看了對應的源代碼―一個龐大的C語言程序-並且決定,也許是愚蠢的決定,從發送消息的代碼中刪除有關寄件人名字的所有引用, 然後重新編譯。

 

Aided by a teaching assistant, I thus embarked on a task which seemed obvious enough although not commonly taught in software engineering courses: systematic program deconstruction. Sure enough, we quickly found the first place where the program accessed the sender’s name, and we removed the corresponding code. This, we naively thought, would have done the job, so we recompiled and sent a test mail message; but the sender’s name was still there! Thus began a long and surreal process: time and again, believing we had finally found the last reference to the sender’s name, we would remove it, recompile, and mail a test message, only to find the name duly recorded once again in its habitual field. Like the Hydra in its famous fight, the mailer kept growing a new head every time we thought we had cut the last neck.

就此,在一個助教的幫助下我開始着手於這個工作,這個工作雖然沒有普遍的在軟件工程課程-系統程序的解析 (deconstruction)-中講授過,但是卻顯而易見。我們迅速地發現了程序存取寄件人名字的第一個位置,並刪除了相關的代碼,這當然足夠了。我們很自然地認爲,這就完成了工作,因此,我們重新編譯併發送了一個測試郵件;但是寄件人的名字仍然出現了!如此開始了一個漫長的和夢幻般的過程: 反覆之後, 我們確信最終找到了寄件人名字的最後一個引用,刪除了它,再重新編譯,併發出了測試郵件,不料竟會再一次發現名字仍舊出現在它應有的位置上。就像在那著名戰鬥中的九頭怪蛇,每當我們認爲已經砍下了最後的一個頭的時候,郵寄者又長出了一個新的。

 

Finally, repeating for the modern era the earlier feat of Hercules, we slew the beast for good; by then we had removed more than twenty code extracts which all accessed, in some way or other, information about the message sender.

最後,就像大力神的壯舉在現代社會中的重演,我們永久地殺死了怪獸;到那時候我們已經刪除了超過二十處的代碼,這些代碼用了某種方法來存取了有關寄件人的信息。

[希臘神話] 許德拉,希臘神話中的九頭怪,斬去一頭會生出二頭,後被大力神赫克勒斯所殺。

 

Although the previous sections have only got us barely started on our road to abstract data types, it should be clear by now that any program written in accordance with even the most elementary concepts of data abstraction would treat MAIL_MESSAGE as a carefully defined abstract notion, supporting a query operation, perhaps called sender, which returns information about the message sender. Any portion of the mail program that needs this information would obtain it solely through the sender query. Had the mail program been designed according to this seemingly obvious principle, it would have been sufficient, for the purpose of my little exercise, to modify the code of the sender query. Most likely, the software would also then have provided an associated command operation set_sender to update sender information, making the job even easier.

在通向抽象數據類型的道路上,剛纔的經過只是一個小插曲依照數據抽象最基本概念而寫出的任何程序,都會把MAIL_MESSAGE 視爲一個切實定義的抽象概念,它支持一個查詢操作,也許稱之爲sender,其返回關於寄件人的信息。這在現在看來是再清楚不過了的。郵件程序中任何需要此信息的部分都將會通過sender的查詢獨立地獲得。要是郵件程序依照這項顯而易見的原則來設計的話,對付我這個小小的練習,則修改sender查詢的代碼就足夠了。如有可能,軟件也會提供一個相關的運算命令set_sender,來更新寄件人的信息,讓工作更容易。

 

What is the real moral of that little story (besides lowering the reader’s guard in preparation for the surprise mathematical offensive of the next section)? After all, the mail program in question is successful, at least judging by its widespread use. But it typifies the current quality standard in the industry. Until we move significantly beyond that standard, the phrase “software engineering” will remain a case of wishful thinking.

這個小故事的真正寓意是什麼呢(除了降低一下讀者的戒心,準備下一段的令人昏昏欲睡的數學攻勢)?畢竟,至少由它廣泛的使用性來判斷的話,討論中的郵件程序是成功的。但是它代表着當前產業中的品質標準。直到我們有效地超越那個標準之前,短語“軟件工程”將保持着一種可望而不可及的狀況。

 

Oh yes, one more note. Some time after my brief encounter with the mail program, I read that certain network hackers had intruded into the computer systems of highly guarded government laboratories, using a security hole of that very mail program — a hole which was familiar, so the press reported, to all those in the know. I was not in the know; but, when I learned the news, I was not surprised.

哦是的,還有一個要注意的。在我短暫地接觸了郵件程序的一段時間之後,我得知某個網絡黑客侵入了高度保護着的政府實驗室內的計算機系統,使用的正是那個郵件程序的安全漏洞-正如新聞媒體所報導的,這是一個內情人所熟知的漏洞。我並不知道內情,但是,當我看到新聞的時候,我並不喫驚。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章