面向對象軟件構造(第2版)-第5章 接近對象技術 (上)

Extendibility, reusability and reliability, our principal goals, require a set of conditions defined in the preceding chapters. To achieve these conditions, we need a systematic method for decomposing systems into modules.

擴充性,複用性和可靠性,要達到這些主要目標需要一系列的條件,這些條件在前面的章節中已定義過了.爲了要完成這些條件,我們需要一個系統的方法來把系統分解成模塊.

 

This chapter presents the basic elements of such a method, based on a simple but far-reaching idea: build every module on the basis of some object type. It explains the idea, develops the rationale for it, and explores some of the immediate consequences.

本章介紹有關這種方法的基本元素,這基於一個簡單而深遠的思想: 以一些對象類型爲基礎建立每個模塊.本章解釋了這個思想,逐步展開其原理,並研究了當前的一些結論.

 

A word of warning. Given today’s apparent prominence of object technology, some readers might think that the battle has been won and that no further rationale is necessary. This would be a mistake: we need to understand the basis for the method, if only to avoid common misuses and pitfalls. It is in fact frequent to see the word “object-oriented” (like “structured” in an earlier era) used as mere veneer over the most conventional techniques. Only by carefully building the case for object technology can we learn to detect improper uses of the buzzword, and stay away from common mistakes reviewed later in this chapter.

注意.今天的對象技術已經有着顯然的地位,一些讀者可能認爲已經是贏得了競爭, 進一步的理論已經沒有必要了.這是一個錯誤:我們需要理解方法的基本原理只是爲了要避免常見的誤用和陷阱.事實上頻繁地看到面向對象的(object-oriented)這個詞(就如早期的"結構化(structured)")僅僅被用於裝飾最傳統的技術.只有通過使用對象技術仔細地構建案例,我們才能學會發現這些術語的不當使用,並且能夠避免常見的錯誤,這在這章稍後討論.

 

5.1 THE INGREDIENTS OF COMPUTATION

5.1 計算的要素

 

The crucial question in our search for proper software architectures is modularization: what criteria should we use to find the modules of our software?

在我們研究恰當的軟件結構中,決定性的問題是模塊化: 我們應該使用什麼樣的標準得到我們的軟件模塊?

 

To obtain the proper answer we must first examine the contending candidates.

要得到正確的回答,我們先要分析這些備選答案.

 

The basic triangle

基礎三角

 

Three forces are at play when we use software to perform some computations:

當我們利用軟件來完成某些計算時,三種力量在起作用:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

To execute a software system is to use certain processors to apply certain actions to certain objects.

要執行一個軟件系統,就要使用特定的處理器(processors)來應用特定的動作(actions)到特定的對象(objects).

 

The processors are the computation devices, physical or virtual, that execute instructions. A processor can be an actual processing unit (the CPU of a computer), a process on a conventional operating system, or a “thread” if the OS is multi-threaded.

處理器是物理的或虛擬的,能執行指令的計算裝置.一個處理器可以是實際的處理單元(計算機的CPU),一個常規操作系統上的進程,或多線程操作系統上的一個線程.

 

The actions are the operations making up the computation. The exact form of the actions that we consider will depend on the level of granularity of our analysis: at the hardware level, actions are machine language operations; at the level of the hardware-software machine, they are instructions of the programming language; at the level of a software system, we can treat each major step of a complex algorithm as a single action.

動作是構成計算過程的運算.我們所考慮的動作的精確形態取決於我們的分析間隔的層次:在硬件層次,動作是機器語言的運算;在硬件-軟件機器的層次,其是程序語言的指令;在軟件系統的層次,我們把一個複雜算法的每個主要的步驟視爲一個單一的動作.

 

The objects are the data structures to which the actions apply. Some of these objects, the data structures built by a computation for its own purposes, are internal and exist only while the computation proceeds; others (contained in the files, databases and other persistent repositories) are external and may outlive individual computations.

對象是動作應用的數據結構.其中的一些對象,計算過程構建的數據結構是出於其自身的目的,它們是內部的並且只存在於計算髮生的時候;其它的(包含在文件,數據庫和其它永久儲存容器中)是外部的,並能永久存活於不同的計算過程中.

 

Processors will become important when we discuss concurrent forms of computation, in which several sub-computations can proceed in parallel; then we will need to consider two or more processors, physical or virtual. But that is the topic of a later chapter; for the moment we can limit our attention to non-concurrent, or sequential computations, relying on a single processor which will remain implicit.

當我們討論計算過程的並行(concurrent)形式的時候,處理器會變得很重要,一些子計算過程能在其中平行處理;接下來我們將會考慮二個或更多的,物理的或虛擬的處理器.但那是後續章節的主題;此時,要把我們的注意力集中在非並行(non-concurrent),循序計算(sequential computations),它運行在一個保持固定的單一處理器.

 

This leaves us with actions and objects. The duality between actions and objects — what a system does vs. what it does it to — is a pervasive theme in software engineering.

現在剩下了動作和對象.在動作和對象之間的二元性-一個系統所做的如何做-是軟件工程的一個普遍的主題.

 

A note of terminology. Synonyms are available to denote each of the two aspects: the word data will be used here as a synonym for objects; for action the discussion will often follow common practice and talk about the functions of a system.

術語註解.同義字可用來表示二個不同角度的任何一方: 單詞data這裏會用作objects的同義字;對於action討論通常按照慣例,同時談論系統的functions.

 

The term “function” is not without disadvantages, since software discussions also use it in at least two other meanings: the mathematical sense, and the programming sense of subprogram returning a result. But we can use it without ambiguity in the phrase the functions of a system, which is what we need here.

由於軟件討論過程中使用至少其它的個含,術語function有這樣的問題: 數學的意義,帶有返回值的子程序的編程意義.但是,這裏我們所需要用到的一個短語,一個系統的函數(the functions of a system),卻沒有什麼歧義.

 

The reason for using this word rather than “action” is the mere grammatical convenience of having an associated adjective, used in the phrase functional decomposition. “Action” has no comparable derivation. Another term whose meaning is equivalent to that of “action” for the purpose of this discussion is operation.

使用這個單詞而不用動作的理由僅僅是語法上的便利,它有一個關聯的形容詞用在短語函數分解(functional decomposition).動作沒有相關的詞源.在這個討論中,另外一個意義和動作相同之術語是運算(operation).

 

Any discussion of software issues must account for both the object and function aspects; so must the design of any software system. But there is one question for which we must choose — the question of this chapter: what is the appropriate criterion for finding the modules of a system? Here we must decide whether modules will be built as units of functional decomposition, or around major types of objects.

軟件議題的任何討論必須對對象及函數兩方面進行闡述;任何軟件系統的設計也一樣. 但是這裏有一個我們一定要作出選擇的問題,也是本章的問題:什麼是決定一個系統模塊的適當準則?在這裏我們必須決定是否模塊作爲函數分解的單元而被構建,還是圍繞主要的對象類型被構建.

 

From the answer will follow the difference between the object-oriented approach and other methods. Traditional approaches build each module around some unit of functional decomposition — a certain piece of the action. The object-oriented method, instead, builds each module around some type of objects.

從回答中將會發覺面向對象的方式和其它方法之間的不同.傳統的方法圍繞着一些函數分解的單元-特定的活動片段-構建每個模塊.不同的是,面向對象的方法圍繞着一些對象類型建立每個模塊.

 

This book, predictably, develops the latter approach. But we should not just embrace O-O decomposition because the title of the book so implies, or because it is the “in” thing to do. The next few sections will carefully examine the arguments that justify using object types as the basis for modularization — starting with an exploration of the merits and limitations of traditional, non-O-O methods. Then we will try to get a clearer understanding of what the word “object” really means for software development, although the full answer, requiring a little theoretical detour, will only emerge in the next chapter.

可以看出,本書介紹後者.但是我們不應該僅僅包含OO分解,只是因爲書名有所暗示,或因爲它正是我們要做的事情.下面的幾個部分將會仔細地分析這些論證,它們驗證了使用對象類型作爲模塊化的基礎-從探索其價值和傳統的,非OO方法的限制開始.然後,對於對象這個單詞對軟件開發而言真正的意義是什麼,我們將會試着得到一個更清晰的理解,雖然完整的答案會在下一章中出現,這需要稍微的理論變換.

 

We will also have to wait until the next chapter for the final settlement of the formidable and ancient fight that provides the theme for the rest of the present discussion: the War of the Objects and the Functions. As we prepare ourselves for a campaign of slander against the functions as a basis for system decomposition, and of corresponding praise for the objects, we must not forget the observation made above: in the end, our solution to the software structuring problem must provide space for both functions and objects — although not necessarily on an equal basis. To discover this new world order, we will need to define the respective roles of its first-class and second-class citizens.

我們也將不得不等待,直到下一章中那強大的和古老的爭鬥的最終結束:對象和函數之間的戰爭,這也是當前討論中其它部分的主題.當我們爲自己準備一個對抗的藉口來反對以函數作爲系統分解的基礎,並且對應地讚美對象的時候,我們不能忘記上述的結果: 最終, 我們對軟件結構問題的解決方案必須對函數和對象兩者都要提供空間-雖然不必相等.爲了獲得這個新世界的次序,我們將分別定義一等和二等公民的不同角色.

 

5.2 FUNCTIONAL DECOMPOSITION

5.2 函數分解

 

We should first examine the merits and limitations of the traditional approach: using functions as a basis for the architecture of software systems. This will not only lead us to appreciate why we need something else — object technology — but also help us avoid, when we do move into the object world, certain methodological pitfalls such as premature operation ordering, which have been known to fool even experienced O-O developers.

使用函數作爲軟件系統架構的一種基礎,我們應該首先調查這種傳統方式的價值和限制.這不但會引導我們去重視我們需要其它的技術-對象技術-的原因,而且,當我們進入到對象世界的時候,這會幫助我們避免某些方法論的陷阱,諸如不成熟的運算順序,它愚弄富有經驗的OO開發者已是世人皆知.

 

Continuity

連續性

 

A key element in answering the question “should we structure systems around functions or around data?” is the problem of extendibility, and more precisely the goal called continuity in our earlier discussions. As you will recall, a design method satisfies this criterion if it yields stable architectures, keeping the amount of design change commensurate with the size of the specification change.

"我們應該是圍繞函數還是圍繞數據來構成系統?",回答這個提問的一個關鍵元素是擴充性的問題, 在我們先前的討論中更明確的目標應該是連續性.正如你所知,如果一個設計方法產生穩定的結構並且保持規格變化的大小等同於設計變化的數量,那麼它就滿足這一個準則.

 

Continuity is a crucial concern if we consider the real lifecycle of software systems, including not just the production of an acceptable initial version, but a system’s long-term evolution. Most systems undergo numerous changes after their first delivery. Any model of software development that only considers the period leading to that delivery and ignores the subsequent era of change and revision is as remote from real life as those novels which end when the hero marries the heroine — the time which, as everyone knows, marks the beginning of the really interesting part.

如果我們考慮軟件系統的真正生命週期,不但包括產品的可接受的初始版本,還包括系統的長期演化,那麼連續性是一個至關重大的要點.絕大多數的系統在它們的第一次發佈之後經歷了很多的變化.軟件發展的任何模型,如果只考慮了發佈之前的時期而忽略了隨後的變化和修改的階段,那麼就和那些英雄與女主角一旦結合就結束的小說一樣,遠離了真正的生活-正如世人所知,這時候纔是真正有趣部份的開始.

[譯註]:是地獄的開始吧.

 

To evaluate the quality of an architecture (and of the method that produced it), we should not just consider how easy it was to obtain this architecture initially: it is just as important to ascertain how well the architecture will weather change.

爲了要評估架構(和它的產生方法)的品質,我們不應該僅僅考慮最初獲得這個架構的便利性:真正重要的是確定架構將會如何合理地應對變化.

 

The traditional answer to the question of modularization has been top-down functional decomposition, briefly introduced in an earlier chapter. How well does top-down design respond to the requirements of modularity?

有關模塊化的問題,傳統的答案是由上而下的函數分解,在之前的內容中曾簡短地介紹過.由上而下的設計如何能較好地應對模塊化的需求呢?

 

Top-down development

由上而下的開發

 

There was a most ingenious architect who had contrived a new method for building houses, by beginning at the roof, and working downwards to the foundation, which he justified to me by the like practice of those two prudent insects, the bee and the spider.

Jonathan Swift: Gulliver’s Travels, Part III, A

Voyage to Laputa, etc., Chapter 5.

 

有一位極聰明的建築師,爲建築房子發明了一個新的方法,從屋頂開始,向下工作到地基,他向我證實了,蜜蜂和蝙蝠,這兩種謹慎的昆蟲也有類似的習性.

Jonathan Swift: 格列佛遊記, Part III, A

Voyage to Laputa, etc., Chapter 5.

 

The top-down approach builds a system by stepwise refinement, starting with a definition of its abstract function. You start the process by expressing a topmost statement of this function, such as

 [C0]

“Translate a C program to machine code”

or:

[P0]

“Process a user command”

and continue with a sequence of refinement steps. Each step must decrease the level of abstraction of the elements obtained; it decomposes every operation into a combination of one or more simpler operations. For example, the next step in the first example (the C compiler) could produce the decomposition

[C1]

“Read program and produce sequence of tokens”

“Parse sequence of tokens into abstract syntax tree”

“Decorate tree with semantic information”

“Generate code from decorated tree”

or, using an alternative structure (and making the simplifying assumption that a C program is a sequence of function definitions):

[C'1]

from

“Initialize data structures”

until

“All function definitions processed”

loop

“Read in next function definition”

“Generate partial code”

end

“Fill in cross references”

由上而下的方法通過逐步求精法(stepwise refinement)構建一個系統,起始於一個抽象函數的定義.通過表達這個函數的一個最頂層的陳述開始這個過程:

[C0]

“Translate a C program to machine code”

:

[P0]

“Process a user command”

下面是一個連續的細化步驟.每一個步驟必須降低所得元素的抽象層次;把每一個運算分解成一個或多個更簡化的運算組合.例如,在第一個例子中(C 編譯器)的下一步能產生如下分解

[C1]

“Read program and produce sequence of tokens”

“Parse sequence of tokens into abstract syntax tree”

“Decorate tree with semantic information”

“Generate code from decorated tree”

或者,使用一個選擇性結構(並且做出簡單假定:一個C程序是一個連續的函數定義):

[C'1]

from

“Initialize data structures”

until

“All function definitions processed”

loop

“Read in next function definition”

“Generate partial code”

end

“Fill in cross references”

 

In either case, the developer must at each step examine the remaining incompletely expanded elements (such as “Read program ¼and “All function definitions processed”) and expand them, using the same refinement process, until everything is at a level of abstraction low enough to allow direct implementation.

在任一情況,開發者必須在每個步驟中檢查所得到的並沒完全擴展的(“Read program ¼“All function definitions processed”),同時使用相同的細化過程展它,直到每個元素抽象層次上足夠到能允許其直接實現.

 

We may picture the process of top-down refinement as the development of a tree. Nodes represent elements of the decomposition; branches show the relation “B is part of the refinement of A”.

我們可以像樹的展開一樣描繪由上而下的細化過程.節點表示分解的元素;分支顯示關係BA細化的一部份".

 

 


The top-down approach has a number of advantages. It is a logical, well-organized thought discipline; it can be taught effectively; it encourages orderly development of systems; it helps the designer find a way through the apparent complexity that systems often present at the initial stages of their design.

由上而下的方式有許多的優點.它是一種合乎邏輯的,組織優異的思維訓練;它能有效地傳授;它能鼓勵系統的有序開發;它幫助設計者尋找一個方法來克服明顯的複雜性,系統時常在開始設計階段就呈現出這種複雜性.

 

The top-down approach can indeed be useful for developing individual algorithms. But it also suffers from limitations that make it questionable as a tool for the design of entire systems:

由上而下的方式對開發獨立的算法是真正有效的.但是,用作一個設計整體系統的工具,它也遭受到了可疑性的限制:

 

• The very idea of characterizing a system by just one function is subject to doubt.

單單只是一個函數就能完整地表示一個系統的想法受到了人們的懷疑.

 

By using as a basis for modular decomposition the properties that tend to change the most, the method fails to account for the evolutionary nature of software systems.

通過使用模組分解爲基礎以試圖最大幅的改變特性,此方法不能解釋軟件系統的演化本性.

 

Not just one function

不僅僅一個函數

 

In the evolution of a system, what may originally have been perceived as the system’s main function may become less important over time.

在系統的化中,那些原本被認爲是系統的主要函數,會隨着時間的流逝變得不再重要.

 

Consider a typical payroll system. When stating his initial requirement, the customer may have envisioned just what the name suggests: a system to produce paychecks from the appropriate data. His view of the system, implicit or explicit, may have been a more ambitious version of this:

考慮一個典型的薪資系統.當描述原始需求的時候,客戶可以想象得到系統名字所描述的含義:一個在恰當的數據中計算工資單的系統.他對系統的看法,不管是隱含的還是明確的,都可以要求一個更高的版本:

 


The system takes some inputs (such as record of hours worked and employee information) and produces some outputs (paychecks and so on). This is a simple enough functional specification, in the strict sense of the word functional: it defines the program as a mechanism to perform one function — pay the employees. The top-down functional method is meant precisely for such well-defined problems, where the task is to perform a single function — the “top” of the system to be built.

系統使用一些輸入(如按小時的工作記錄和職員信息)併產生一些輸出(工資單等等). 在單詞functional的嚴格的含義中,這是一個簡單充分的函數規格: 它以一種細述程序的方法來完成一個支付員工薪水的函數.對這種定義明確的問題而言,由上而下的函數分解方法意味着精確性,這裏的任務是要完成一個單一的函數―構建系統的頂端.

 

Assume, however, that the development of our payroll program is a success: the program does the requisite job. Most likely, the development will not stop there. Good systems have the detestable habit of giving their users plenty of ideas about all the other things they could do. As the system’s developer, you may initially have been told that all you had to do was to generate paychecks and a few auxiliary outputs. But now the requests for extensions start landing on your desk: Could the program gather some statistics on the side? I did tell you that next quarter we are going to start paying some employees monthly and others biweekly, did I not? And, by the way, I need a summary every month for management, and one every quarter for the shareholders. The accountants want their own output for tax preparation purposes. Also, you are keeping all this salary information, right? It would be really nifty to let Personnel access it interactively. I cannot imagine why that would be a difficult functionality to add.

然而,假定我們薪資程序的開發是成功的: 程序各盡其職.但最有可能的是開發並沒有就此結束.優秀的系統帶給用戶可惡的習慣,又讓他們產生了許多額外工作的想法.身爲系統的開發者,您最初可能已經被告之您必須做的全部工作就是要生成工資單和一些附加的輸出.但是現在在您的書桌上開始擺放着擴充的需求了:程序可以採集另外的一些統計數據嗎?我得告訴您下個季度我們開始支付一些職員月薪而其他的是雙週薪,可以嗎?並且,順便提一下,我需要爲管理層提供月報表,併爲股東提供季報.爲納稅準備的會計需要他們自己的輸出格式.您也能保存全部的薪水信息,對嗎? 讓員工交互地使用軟件將會是真正的好主意,我不能夠想象爲什麼那個功能加上去會很困難.

 

This phenomenon of having to add unanticipated functions to successful systems occurs in all application areas. A nuclear code that initially just applied some algorithm to produce tables of numbers from batch input will be extended to handle graphical input and output or to maintain a database of previous results. A compiler that just translated valid source into object code will after a while double up as a syntax verifier, a static analyzer, a pretty-printer, even a programming environment.

這種不得不把事先未預料到的功能加入成功的系統中的現象在所有的應用領域中都會發生.一個核心代碼,最初只是提供算法用來從批輸入中產生數目表,將會擴展至處理圖像的輸入和輸出,或維護以前結果的數據庫.一個僅僅是把有效的源代碼編譯成對象碼的編譯器,不久將會塞進一個語法驗證器,一個靜態分析器,一個漂亮的打印機程序,更甚至一個編程環境.

 

This change process is often incremental. The new requirements evolve from the initial ones in a continuous way. The new system is still, in many respects, “the same system” as the old one: still a payroll system, a nuclear code, a compiler. But the original “main function”, which may have seemed so important at first, often becomes just one of many functions; sometimes, it just vanishes, having outlived its usefulness.

這種變化過程經常是漸進式的.新需求用一種連續的方式從初始需求就開始了.新系統在許多方面和以前相比一直是同一個系統:仍舊是一個薪資系統,一個核心代碼,一個編譯器.但是最初的主要功能,開始時看上去相當的重要,但常常只是變成許多功能中的一個;有時,在事過境遷後就無影無蹤了.

 

If analysis and design have used a decomposition method based on the function, the system structure will follow from the designers’ original understanding of the system’s main function. As the system evolves, the designers may feel sorry (or its maintainers, if different people, may feel angry) about that original assessment. Each addition of a new function, however incremental it seems to the customer, risks invalidating the entire structure.

如果分析和設計已經使用了基於函數的分解方法,那麼系統結構將反映了設計者對系統主要函數的最初理解.隨着系統的進展,設計者可能對那個最初的看法感到不甚滿意(或者,如果它的維護者是另外的人,可能也會覺得不滿).然而,每一個新功能的加入,似乎滿足了客戶,卻冒着使整個結構變得糟糕的風險。

 

It is crucial to find, as a criterion for decomposition, properties less volatile than the system’s main function.

作爲一條系統分解的準則,關鍵是尋找比系統的主函數更穩定的屬性.

 

Finding the top

尋找頂端

 

Top-down methods assume that every system is characterized, at the most abstract level, by its main function. Although it is indeed easy to specify textbook examples of algorithmic problems — the Tower of Hanoi, the Eight Queens and the like — through their functional “tops”, a more useful description of practical software systems considers each of them as offering a number of services. Defining such a system by a single function is usually possible, but yields a rather artificial view.

由上而下的方法假定,在最抽象的層次上,系統的主函數表現了系統的主要特徵.儘管教科書上的算法例子-漢內塔,八皇后和類似的-通過它們的函數的頂端很容易的就能指明這些特徵,但是一個更實用的實際軟件系統的描述會考慮其中的每一個特徵都要提供許多的服務.通過一個單一的函數定義這樣的一個系統通常是可能的,但是卻讓人認爲相當的不真實.

 

Take an operating system. It is best understood as a system that provides certain services: allocating CPU time, managing memory, handling input and output devices, decoding and carrying out users’ commands. The modules of a well-structured OS will tend to organize themselves around these groups of functions. But this is not the architecture that you will get from top-down functional decomposition; the method forces you, as the designer, to answer the artificial question “what is the topmost function?”, and then to use the successive refinements of the answer as a basis for the structure. If hard pressed you could probably come up with an initial answer of the form

“Process all user requests”

which you could then refine into something like

from

boot

until

halted or crashed

loop

“Read in a user’s request and put it into input queue”

“Get a request r from input queue”

“Process r

“Put result into output queue”

“Get a result o from output queue”

“Output o to its recipient”

end

就拿一個操作系統來說.最能想到的是系統提供的下列服務:分派處理器時間,管理內存,操作輸入和輸出設備,解碼和執行用戶的指令.一個具有優良結構的操作系統的模塊將會傾向於在這些功能羣體的周圍組織它們自己.但是,您並不能從由上而下的函數分解中獲得這樣的架構;作爲設計者,此方法強迫您回答虛僞的問題最頂端的函數是什麼?,然後將答案連續細化作爲一種結構的基礎.如果在巨大的壓力下,您可能會提出“Process all user requests”形式的一個起始回答,接着您能細化成下列這樣:

from

boot

until

halted or crashed

loop

“Read in a user’s request and put it into input queue”

“Get a request r from input queue”

“Process r

“Put result into output queue”

“Get a result o from output queue”

“Output o to its recipient”

end

 

Refinements can go on. From such premises, however, it is unlikely that anyone can ever develop a reasonably structured operating system.

細化能繼續進行.然而,在這樣的前提之下,開發出一個合理結構的操作系統是不可能的.

 

Even systems which may at first seem to belong to the “one input, one abstract function, one output” category reveal, on closer examination, a more diverse picture. Consider the earlier example of a compiler. Reduced to its bare essentials, or to the view of older textbooks, a compiler is the implementation of one input-to-output function: transforming source text in some programming language into machine code for a certain platform. But that is not a sufficient view of a modern compiler. Among its many services, a compiler will perform error detection, program formatting, some configuration management, logging, report generation.

更甚至,在進一步的檢驗中,起先看上去屬於一個輸入,一個抽象函數,一個輸出 之類的系統,卻顯示了一個完全不同的景象.考慮先前的編譯器例子.撥開外表看本質,或究其根源,一個編譯器是一個輸入到輸出功能的實現: 把一些程序語言的源代碼轉換成某個平臺的機器碼.但在現代的編譯器觀點中這並不充份.在衆多的服務之中,一個編譯器要完成錯誤診測,程序格式化,一些配置管理,記錄日誌,生成報表.

 

Another example is a typesetting program, taking input in some text processing format — TEX, Microsoft Word, FrameMaker ¼ — and generating output in HTML, Postscript or Adobe Acrobat format. Again we may view it at first as just an input-to-output filter. But most likely it will perform a number of other services as well, so it seems more interesting, when we are trying to characterize the system in the most general way, to consider the various types of data it manipulates: documents, chapters, sections, paragraphs, lines, words, characters, fonts, running heads, titles, figures and others.

另外的一個例子是個排版程序,輸入一些文字處理格式-TEX,Microsoft Word, FrameMaker同時產生HTML,Postscript或Adobe Acrobat格式的輸出.開始我們可以再次把它看成是一個輸入輸出過濾器.但是很有可能它也完成了衆多的其它服務, 因此,當我們試圖用通常的方法來刻畫系統特點的時候,會考慮它操縱的各種不同的數據類型:文件,章節,段落,小節,一行,單詞,字符,字體,頁頭,標題,插圖和其它,這看上去更加有趣.

 

The seemingly obvious starting point of top-down design — the view that each new development fulfills a request for a specific function — is subject to doubt:

很明顯的,由上而下設計的出發點-對每個新的開發實現了特定功能的需求之觀點-是值得懷疑的:

Real systems have no top.

真正的系統沒有頂端.

 

 

 

 

Functions and evolution

函數演化

 

Not only is the main function often not the best criterion to characterize a system initially: it may also, as the system evolves, be among the first properties to change, forcing the top-down designer into frequent redesign and defeating our attempts to satisfy the continuity requirement.

對於表示一個系統的初始特徵,主函數不但通常不是最好的準則,而且它很可能在系統進展之中是第一個被改變的屬性,這強迫由上而下的設計者頻繁地重新設計,並讓我們在滿足連續性需求的嘗試中遭到失敗.

 

Consider the example of a program that has two versions, a “batch” one which handles every session as a single big run over the problem, and an interactive one in which a session is a sequence of transactions, with a much finer grain of user-system communication. This is typical of large scientific programs, which often have a “let it run a big chunk of computation for the whole night” version and a “let me try out a few things and see the results at once then continue with something else” version.

考慮一個例子,一個程序有二個版本,一個批處理(batch)版本,把每一個會話作爲一個單一的巨大的超限問題來處理;一個交互版本,其中一個會話就是一個順序的事務,對用戶-系統之間溝通具有清楚的條理.這是典型的大型科學系統的程序,通常有讓它整晚地運行一個大計算的版本和讓我試試一些東西並立刻看到結果然後繼續的版本。

 

The top-down refinement of the batch version might begin as

批處理版本的由上而下的細化可能開始於如下:

 

[B0] -- Top-level abstraction

“Solve a complete instance of the problem”

[B1] -- First refinement

“Read input values”

“Compute results”

“Output results”

 

and so on. The top-down development of the interactive version, for its part, could proceed in the following style:

等等.交互版本的由上而下的開發部分過程如下列形式:

 [I1]

“Process one transaction”

[I2]

if  “New information provided by the user” then

“Input information”

“Store it”

elseif  “Request for information previously given” then

“Retrieve requested information”

“Output it”

elseif  “Request for result” then

if “Necessary information available” then

“Retrieve requested result”

“Output it”

else

“Ask for confirmation of the request”

if Yes then

“Obtain required information”

“Compute requested result”

“Output result”

end

end

else

(Etc.)

 

Started this way, the development will yield an entirely different result. The top-down approach fails to account for the property that the final programs are but two different versions of the same software system — whether they are developed concurrently or one has evolved from the other.

這樣開始的話,開發將會產生一個完全不同的結果.由上而下的方式沒有解釋這個特性,既最終的程序是同一個軟件系統的二個不同版本-是否它們同時開發,或是一個從另一個演化而來.

 

This example brings to light two of the most unpleasant consequences of the top-down approach: its focus on the external interface (implying here an early choice between batch and interactive) and its premature binding of temporal relations (the order in which actions will be executed).

這個例子揭露了由上而下方式中最討厭的兩種結果:它着眼於外部接口(在這裏是指在批處理和交互式之間的過早選擇)和它在臨時關係上(運行動作的順序)草率的綁定.

 

Interfaces and software design

接口和軟件設計

 

System architecture should be based on substance, not form. But top-down development tends to use the most superficial aspect of the system — its external interface — as a basis for its structure.

系統架構應該以實質而不是以形式爲基礎.但是由上而下開發傾向使用系統最表面的方面-其外部接口-當作結構的基礎.

 

The focus on external interfaces is inevitable in a method that asks “What will the system do for the end user?” as the key question: the answer will tend to emphasize the most external aspects.

在一個把系統將會爲最終用戶做什麼?作爲關鍵問題的方法中,焦點集中在外部接口上是不可避免的: 答案傾向於強調最外部的方面.

 

The user interface is only one of the components of a system. Often, it is also among the most volatile, if only because of the difficulty of getting it right the first time; initial versions may be of the mark, requiring experimentation and user feedback to obtain a satisfactory solution. A healthy design method will try to separate the interface from the rest of the system, using more stable properties as the basis for system structuring.

用戶接口只是系統的組件之一.如果只因爲開始時理解有困難,那麼它也常常是最不穩定的因素之一;初始版本可能是樣品,需要實驗和用戶反饋以獲得滿意的解決方案. 一個健壯的設計方法會設法把接口與系統的其餘部分分開,使用更加穩定的屬性作爲系統結構的基礎.

 

It is in fact often possible to build the interface separately from the rest of the system, using one of the many tools available nowadays to produce elegant and user-friendly interfaces, often based on object-oriented techniques. The user interface then becomes almost irrelevant to the overall system design.

事實上,接口系統的其餘部分中分離,並使用當前有效的工具產生精緻的用戶友好的接口,基於面向對象技術來說是可行的.那麼用戶接口幾乎和整個系統設計無關了.

 

Premature ordering

早期次序

 

The preceding examples illustrate another drawback of top-down functional decomposition: premature emphasis on temporal constraints. Each refinement expands a piece of the abstract structure into a more detailed control architecture, specifying the order in which various functions (various pieces of the action) will be executed. Such ordering constraints become essential properties of the system architecture; but they too are subject to change.

先前的例子說明了由上而下的函數分解中的另外一個缺點:過早的強調了時間約束. 每個細化從一小塊抽象結構擴展到一個較爲詳細的控制結構,同時指定了各種不同的函數(各種不同的動作)將會執行的順序.這樣的次序約束形成了系統架構的基本屬性;但是它們也受制於變化.

 

Recall the two alternative candidate structures for the first refinement of a compiler:

對一個編譯器的首次細化,調用二種可能的候選結構:

 

[C1]

“Read program and produce sequence of tokens”

“Parse sequence of tokens into abstract syntax tree”

“Decorate tree with semantic information”

“Generate code from decorated tree”

[C'1]

from

“Initialize data structures”

until

“All function definitions processed”

loop

“Read in next function definition”

“Generate partial code”

end

“Fill in cross references”

 

As in the preceding example we start with two completely different architectures. Each is defined by a control structure (a sequence of instructions in the first case, a loop followed by an instruction in the second), implying strict ordering constraints between the elements of the structure. But freezing such ordering relations at the earliest stages of design is not reasonable. Issues such as the number of passes in a compiler and the sequencing of various activities (lexical analysis, parsing, semantic processing, optimization) have many possible solutions, which the designers must devise by considering space-time tradeoffs and other criteria which they do not necessarily master at the beginning of a project. They can perform fruitful design and implementation work on the components long before freezing their temporal ordering, and will want to retain this sequencing freedom for as long as possible. Top-down functional design does not provide such flexibility: you must specify the order of executing operations before you have had a chance to understand properly what these operations will do.

在剛纔的例子中我們以二個完全地不同的架構開始.每個都被控制結構(在第一種情況中是一個順序的指令,第二種情況中是一個循環指令)所定義,在結構元素之間包含着嚴格的次序約束.但在早期的設計階段就固定這樣的次序關係是不太合理的. 諸如一個編譯器的路徑數目和各種不同動作(詞彙分析,解析,語義處理,最佳化)的順序性,這樣的議題有着許多可能的解決方案,設計者必須通過考慮時間空間的折衷和其它的標準來設計這些方案,在項目的早期階段不必掌握這些折衷和標準.在凍結它們的時序(temporal ordering)之前,設計者能夠在組件的基礎上完成卓有成效的設計和實現工作,並將會盡可能長的保持這個次序自由.由上而下的函數設計不能提供如此的靈活性:在你有機會正確理解這些運算動作之前,你必須指定執行運算的順序.

 

Some design methods that attempt to correct some of the deficiencies of functional top-down design also suffer from this premature binding of temporal relationships. This is the case, among others, with the dataflow-directed method known as structured analysis and with Merise (a method popular in some European countries).

一些設計方法嘗試去改正由上而下的函數設計中的一些缺點,也受困於這個時間關係的早期綁定.其中,作爲結構化分析的直接數據流方法和Merise方法(物流流程分析法,流行於一些歐洲國家),就是這種情況.

 

Object-oriented development, for its part, stays away from premature ordering. The designer studies the various operations applicable to a certain kind of data, and specifies the effect of each, but defers for as long as possible specifying the operations’ order of execution. This may be called the shopping list approach: list needed operations — all the operations that you may need; ignore their ordering constraints until as late as possible in the software construction process. The result is much more extendible architectures.

在這些當中,面向對象的開發避免了早期順序.設計者考慮各種不同的運算應用在一個特定的數據類型上,並詳細說明每一種運算的效果,但是儘可能遲的描述運算的執行順序.這可以稱之爲購物清單(shopping list)方式: 列出了你可能需要所有運算;在軟件構造過程中,儘可能遲的考慮其次序約束.這樣的結果將是個更易於擴充的架構.

 

Ordering and O-O development

次序和OO開發

 

The observations on the risks of premature ordering deserve a little more amplification because even object-oriented designers are not immune. The shopping list approach is one of the least understood parts of the method and it is not infrequent to see O-O projects fall into the old trap, with damaging effects on quality. This can result in particular from misuse of the use case idea, which we will encounter in the study of O-O methodology.

即使面向對象的設計者也不是不受影響的,所以在早期次序的風險方面的調查有着一點點的誇大.購物清單方式是方法中最難理解的部份之一,而且它常常使OO項目陷入原來問題的沼澤之中,同時破壞了在品質上所作的努力.特別的,從用例(use case)的概念上誤用就能看到這種現象,我們將會在OO方法學的研究中遇到用例.

 

The problem is that the order of operations may seem so obvious a property of a system that it will weasel itself into the earliest stages of its design, with dire consequences if it later turns out to be not so final after all. The alternative technique (under the “shopping list” approach), perhaps less natural at first but much more flexible, uses logical rather than temporal constraints. It relies on the assertion concept developed later in this book; we can get the basic idea now through a simple non-software example.

問題是操作次序可能似乎是系統的一個明顯特性,以至於不把它投入到最早的設計階段之中就會產生可怕的後果,但之後證實根本就沒有那麼重要.另一個可選的技術(購物清單方式之後)使用了邏輯限制而非時間上的,也許起先並不出色但卻更加靈活.它依靠在本書中稍後介紹的斷言觀念;現在我們通過一個簡單的非軟件上的例子來理解其基本的思想.

 

Consider the problem of buying a house, reduced (as a gross first approximation) to three operations: finding a house that suits you; getting a loan; signing the contract. With a method focusing on ordering we will describe the design as a simple sequence of steps:

[H1]

find_house

get_loan

sign_contract

 

考慮一個買房子的問題,簡化(成一個大約的近似值)到三個步驟:發現一個適合您的房子;得到貸款;簽署契約.用一個強調次序的方法,我們將會把設計描述成簡單的一系列步驟:

[H1]

find_house

get_loan

sign_contract

 

In the shopping list approach of O-O development we will initially refuse to attach too much importance to this ordering property. But of course constraints exist between the operations: you cannot sign a contract unless (let us just avoid saying until for the time being!) you have a desired house and a loan. We can express these constraints in logical rather than temporal form:

在OO開發的購物清單方式中,我們最初會拒絕在次序屬性上附加太多的重要東西. 當然在操作中約束依然存在: 你不能夠簽署一份契約除非(unless,讓我們暫時避免說until!)你有一個渴望得到的房子和一份貸款.我們能表達這些約束在邏輯上的而非時間上的形式:

 [H'1]

find_property

ensure

property_found

get_loan

ensure

loan_approved

sign_contract

require

property_found and loan_approved

 

The notation will only be introduced formally in chapter 11, but it should be clear enough here: require states a precondition, a logical property that an operation requires for its execution; and ensure states a postcondition, a logical property that will follow from an operation’s execution. We have expressed that each of the first two operations achieves a certain property, and that the last operation requires both of these properties.

這些符號將會在第11章中被正式地介紹,但是這裏表達應該足夠清楚了: require聲明一個前置條件,一個運算爲自己的執行所需要的邏輯屬性; ensure聲明瞭一個後置條件,一個將從運算執行中所得出的邏輯屬性.我們已經描繪了這些運算,頭兩個運算中的每一個都完成一個特定的屬性,最後一個運算需要這兩個屬性.

 

Why is the logical form of stating the constraints, H'1, better than the temporal form, H1? The answer is clear: H'1 expresses the minimum requirements, avoiding the overspecification of H1. And indeed H1 is too strong, as it rules out the scheme in which you get the loan first and then worry about the property — not at all absurd for a particular buyer whose main problem is financing. Another buyer might prefer the reverse order; we should support both schemes as long as they observe the logical constraint.

爲什麼約束聲明的邏輯形式H'1,勝於時間形式H1?答案很清楚: H'1表達了最小量的需求,避免了H1的過度規格.並且,H1的確過於強大,因爲它脫離了計劃,既您先獲得了貸款然後再關心房子-對於一個主要問題是融資的特別買主一點也不荒謬.另外的一個買主可能更喜歡倒序;只要他們遵從邏輯的約束,我們就應該支持這兩種方案.

 

Now imagine that we turn this example into a realistic model of the process with the many tasks involved — title search, termite inspection, pre-qualifying for the loan, finding a real estate agent, selling your previous house if applicable, inviting your friends to the house-warming party¼ It may be possible to express the ordering constraints, but the result will be complicated and probably fragile (you may have to reconsider everything if you later include another task). The logical constraint approach scales up much more smoothly; each operation simply states what it needs and what it guarantees, all in terms of abstract properties.

現在,設想我們把這個例子變成此過程的一個現實模型,模型中包含了許多項工作-地契查尋,白蟻檢查,取得貸款資格,尋找一個不動產代理商,可能的話賣掉您的舊房子,邀請您的朋友參加喬遷宴會這儘可能的表達了那個次序約束,但是這樣的結果會很複雜,並且可能會被打亂(如果稍後又包括了另外一項工作,那麼您不得不重新考慮每件事).邏輯約束的方式增加了更多的平滑性;每個運算只是簡單地表明它所需要的和它所保證的,全部這些都以抽象屬性爲根據.

 

These observations are particularly important for the would-be object designer, who may still be influenced by functional ideas, and might be tempted to rely on early identification of system usage scenarios (“use cases”) as a basis for analysis. This is incompatible with object-oriented principles, and often leads to top-down functional decomposition of the purest form — even when the team members are convinced that they are using an object-oriented method.

這些結論對未來的對象設計者特別地重要,函數的思維一直影響着他們,同時,作爲一種分析基礎的系統使用場景(用例)的早期識別也可能吸引着他們有所依賴.這是和麪向對象的原則相矛盾的,並時常導致最純粹形式的由上而下的函數分解-即使團隊成員確信他們正在使用一個面向對象的方法.

 

We will examine, in our study of O-O methodological principles, what role can be found for use cases in object-oriented software construction.

在我們的OO方法論原則的研究中,我們將會分析在面向對象軟件構造中用例是個什麼樣的角色.

 

Reusability

複用性

 

After this short advance incursion into the fringes of object territory, let us resume our analysis of the top-down method, considering it this time in relation to one of our principal goals, reusability.

短暫地瞭解對象領域的大概之後,讓我們重新開始我們的由上而下方法的分析,這次考慮我們的主要目標之一,複用性.

 

Working top-down means that you develop software elements in response to particular subspecifications encountered in the tree-like development of a system. At a given point of the development, corresponding to the refinement of a certain node, you will detect the need for a specific function — such as analyzing an input command line — and write down its specification, which you or someone else will then implement.

用由上而下的方法工作意味着您所開發的軟件元素反映了特殊的子規格,這會在一個系統的樹狀開發中遇到.在開發過程中一個給定的點上,相應地對某個特定的節點細化,您會發現對特定函數的需求-如分析一個輸入的指令行-並記下它的規格,這些是您或其他人將要去實現的.

 

 


 

The figure, which shows part of a top-down refinement tree, illustrates this property: C2 is written to satisfy some sub-requirement of C; but the characteristics of C2 are entirely determined by its immediate context — the needs of C. For example, C could be a module in charge of analyzing some user input, and C2 could be the module in charge of analyzing one line (part of a longer input).

這個圖形,顯示了一個由上而下的細化樹的一部份,它闡明瞭這個屬性: 編寫C2用來滿足C的一些子需求;但是C2的特徵完全取決於臨近的環境-C的需求.例如,C可能是一個負責分析一些用戶輸入的模塊,那麼C2可能是負責分析其中一行(長輸入的一部份)的模塊.

 

This approach is good at ensuring that the design will meet the initial specification, but it does not promote reusability. Modules are developed in response to specific subproblems, and tend to be no more general than implied by their immediate context. Here if C is meant for input texts of a specific kind, it is unlikely that C2, which analyzes one line of those texts, will be applicable to any other kind of input.

這種方式有效地確保了設計符合初始的規格,但是它並不促進複用性.模塊爲反映特定的子問題而開發,並且試圖比它們的臨近的環境所暗示的功能更通用.在這裏,如果C被定義爲一個特定類型的輸入文本,那它則不太可能像C2那樣分析那些本文中的單行,而是會應用於任何的其它類型的輸入.

 

One can in principle include the concern for extendibility and generality in a top-down design process, and encourage developers to write modules that transcend the immediate needs which led to their development. But nothing in the method encourages generalization, and in practice it tends to produce modules with narrow specifications.

大體而言,在由上而下的設計過程中,此類方法能考慮擴充性和一般性,並鼓勵開發者編寫超越引導他們開發的直接需求的模塊.但是方法並不鼓勵泛化,並且實際上它趨向產生狹義規格的模塊.

 

The very notion of top-down design suggests the reverse of reusability. Designing for reusability means building components that are as general as possible, then combining them into systems. This is a bottom-up process, at the opposite of the top-down idea of starting with the definition of “the problem” and deriving a solution through successive refinements.

由上而下設計的真正的觀念暗含着複用性的相反面.爲複用性而設計意味着構建儘可能通用的組件,然後在系統中組合它們.由上而下的概念則是以問題的定義開始並經過連續的細化得到解決方案,與此相反,這是一個顛倒的過程.

 

This discussion makes top-down design appear as a byproduct of what we can call the project culture in software engineering: the view that the unit of discourse is the individual project, independently of earlier and later projects. The reality is less simple: project n in a company is usually a variation on project n – 1, and a preview of project n + 1. By focusing on just one project, top-down design ignores this property of practical software construction.

這個討論使得由上而下的設計看起來就像是在軟件工程中稱爲項目文化(project culture)的一個副產品: 認爲會話單元是單獨的項目,獨立於之前和之後的項目.事實卻不那麼簡單: 一家公司的項目n通常是在項目n–1上的一種變化,並且是項目n+1的一個預演.由於只着眼於一個項目上,由上而下的設計忽略了實際軟件構造中的這個屬性.

 

Production and description

產品和描述

 

One of the reasons for the original attraction of top-down ideas is that a top-down style may be convenient to explain a design once it is in place. But what is good to document an existing design is not necessarily the best way to produce designs. This point was eloquently argued by Michael Jackson in System Development:

由上而下的思想最初吸引人的理由之一是,在適當的情況下,由上而下的風格可以方便地說明一個設計.但是用之描述一個已存在的方法,並不證明也能用之產生設計.這點被Michael Jackson系統開發中強有力地指出了:

 

Top-down is a reasonable way of describing things which are already fully understood... But top-down is not a reasonable way of developing, designing, or discovering anything. There is a close parallel with mathematics. A mathematical textbook describes a branch of mathematics in a logical order: each theorem stated and proved is used in the proofs of subsequent theorems. But the theorems were not developed or discovered in this way, or in this order...

由上而下是描述一個已經是完全瞭解的事物的合理方法 但是它並不是開發,設計,或研究一切事務之合理方法.在數學上有一個相似的說法.一本數學的教科書以一個邏輯的順序描述一個數學分支: 每個被證明過的定理可被用於證明後來的定理.但是定理並不是以這樣的方式或順序被證明和發現的

 

When the developer of a system, or of a program, already has a clear idea of the completed result in his mind, he can use top-down to describe on paper what is in his head. This is why people can believe that they are performing top-down design or development, and doing so successfully: they confuse the method of description with the method of development... When the top-down phase begins, the problem is already solved, and only details remain to be solved.

一個系統的或程序的開發者,當在他的頭腦中對完整的結果已經有一個清楚的認識時,他能使用由上而下的方式書面地描述出他的思維.這正是人們爲什麼能相信他們一直在進行由上而下的設計或開發,而且做如此的成功: 他們把描述的方法和開發的方法弄混了當由上而下的階段開始的時候,問題其實已經被解決了,只剩下了細節.

 

Top-down design: an assessment

由上而下設計的評估

 

This discussion of top-down functional design shows the method to be poorly adapted to the development of significant systems. It remains a useful paradigm for small programs and individual algorithms; it is certainly a helpful technique to describe well-understood algorithms, especially in programming courses. But it does not scale up to large practical software. By developing a system top-down you trade short-term convenience for long-term inflexibility; you unduly privilege one function over the others; you may be led to devoting your attention to interface characteristics at the expense of more fundamental properties; you lose sight of the data aspect; and you risk sacrificing reusability.

有關由上而下的函數設計的討論表明了此方法不能適應重要的系統開發.它對於小型的程序和獨立的算法倒是一個有用的樣例;對於描述易於理解的算法,尤其在程序設計課程方面,它的確是有效的技術.但是它不能應用到大型的應用軟件中去.通過由上而下的方法來開發一個系統,您會用短期的方便換取長期的穩定性;您會錯誤地給於一個函數超過其它函數的特權;這也許導致您專心於接口特徵,而損害更多的基本屬性;您會對數據視而不見;並且,您會冒着犧牲複用性的風險.

 



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