面向對象軟件構造(第2版)-第4章 複用性方法Approaches to reusability (下)

4.7 TRADITIONAL MODULAR STRUCTURES

4.7 傳統的模塊結構

 

Together with the modularity requirements of the previous chapter, the five requirements of Type Variation, Routine Grouping, Implementation Variation, Representation Independence and Factoring Out Common Behaviors define what we may expect from our reusable components — abstracted modules.

連同先前章節的模塊性需求一起,類型變化,例程分組,實現變化,表示法獨立,合併通用行爲,五個需求定義了我們可以從可複用組件-抽象模塊中所期待的東西.

 

Let us study the pre-O-O solutions to understand why they are not sufficient — but also what we should learn and keep from them in the object-oriented world.

讓我們學習OO之前的解決方案來理解爲什麼它們並不充份-同時在面向對象的領域中防止我們所瞭解的缺陷再次出現.

 

Routines

例程

 

The classical approach to reusability is to build libraries of routines. Here the term routine denotes a software unit that other units may call to execute a certain algorithm, using certain inputs, producing certain outputs and possibly modifying some other data elements. A calling unit will pass its inputs (and sometimes outputs and modified elements) in the form of actual arguments. A routine may also return output in the form of a result in this case it is known as a function.

複用性的經典方式是要建立例程庫.這裏術語例程表示一個軟件單元,其它的單元可以調用它執行一個特定的算法,使用特定的輸入,產生特定的輸出並且可能修改一些其它的數據元素.一個調用單元將會以實參(actual arguments)的形式傳遞它的輸入(輸出,和修改過的元素).一個例程也可能以一個結果(result)的形式返回輸出;在這種情況中它被認爲是一個函數(function).

 

The terms subroutine, subprogram and procedure are also used instead of routine. The first two will not appear in this book except in the discussion of specific languages (the Ada literature talks about subprograms, and the Fortran literature about subroutines.) “Procedure” will be used in the sense of a routine which does not return a result, so that we have two disjoint categories of routine: procedures and functions. (In discussions of the C language the term “function” itself is sometimes used for the general notion of routine, but here it will always denote a routine that returns a result.)

術語子例程(subroutine),子程序(subprogram)過程(procedure)也常用來替代例程.除了討論特定的語言之外(Ada文檔中涉及的是子程序, Fortran文檔中涉及的是子例程.),前兩者在本書中將不再出現.過程將會被用於不返回結果的例程, 所以我們有二種不同的例程: 過程及函數.(在C語言的討論中,術語函數的本身有時作爲例程的通用觀念,但是在這裏它總是指有返回結果的例程.)

 

Routine libraries have been successful in several application domains, in particular numerical computation, where excellent libraries have created some of the earliest success stories of reusability. Decomposition of systems into routines is also what one obtains through the method of top-down, functional decomposition. The routine library approach indeed seems to work well when you can identify a (possibly large) set of individual problems, subject to the following limitations:

例程庫已經成功地運用在一些應用程序領域中,特別是在數值計算中,其中一些優秀的庫開創了一些早期的有關複用性的成功故事.系統分解成例程也可以通過由上而下的,功能分解的方式獲得.當您能識別一(可能是大規模的)系列獨立的問題,並能遵從下列各項限制的時候,例程庫的方式就真正地起作用了:

 

R1 • Each problem admits a simple specification. More precisely, it is possible to characterize every problem instance by a small set of input and output arguments.

R1 每個問題允許一件簡單的規格.更精確的說,儘可能的通過一小組的輸入和輸出參數來描述每個問題實例的特性.

 

R2 • The problems are clearly distinct from each other, as the routine approach does not allow putting to good use any significant commonality that might exist — except by reusing some of the design.

R2 由於例程方法不允許很好地利用任何可能存在的有效共通性-除了被一些設計複用之外,所以問題之間很明顯彼此不同.

 

R3 • No complex data structures are involved: you would have to distribute them among the routines using them, losing the conceptual autonomy of each module.

R3 例程庫不涉及複雜的數據結構:您必須在例程使用之時分配它們,同時也失去了每個模塊上的設計獨立性.

 

The table searching problem provides a good example of the limitations of subroutines. We saw earlier that a searching routine by itself does not have enough context to serve as a stand-alone reusable module. Even if we dismissed this objection, however, we would be faced with two equally unpleasant solutions:

表查詢問題提供了一個很好的子例程限制性的例子.我們起先看見一個查詢例程本身沒有足夠的上下文環境作爲一個獨立可複用的模塊來應用.然而,即使我們消除了這個障礙,我們依然會面對二個相關的,討厭的解決方案:

 

• A single searching routine, which would try to cover so many different cases that it would require a long argument list and would be very complex internally.

一個簡單的查詢例程,要試圖包括如此衆多的不同的情況,這會導致它需要一個很長的參數列表,並且內部也會非常的複雜.

 

• A large number of searching routines, each covering a specific case and differing from some others by only a few details in violation of the Factoring Out Common Behaviors requirement candidate reusers could easily lose their way in such a maze.

在違反合併通用行爲的需求中,很多的查詢例程,每個都包含一種特定的情況並且只有少量細節不同於其它;那些潛在的複用者在這樣的迷宮中會不知所措.

 

More generally, routines are not flexible enough to satisfy the needs of reuse. We have seen the intimate connection between reusability and extendibility. A reusable module should be open to adaptation, but with a routine the only means of adaptation is to pass different arguments. This makes you a prisoner of the Reuse or Redo dilemma: either you like the routine as it is, or you write your own.

更通常的來講,例程不具有足夠的靈活性來滿足複用的需要.我們已經看到複用性和擴充性之間的親密聯繫.一個可複用的模塊應該對改編開放,但是改編的唯一方法是要傳遞不同的參數給例程.這使您陷入複用和重做的難題:或是照現在的樣子使用例程,或是編寫您自己的.

 

Packages

 

In the nineteen-seventies, with the progress of ideas on information hiding and data abstraction, a need emerged for a form of module more advanced than the routine. The result may be found in several design and programming languages of the period the best known are CLU, Modula-2 and Ada. They all offer a similar form of module, known in Ada as the package. (CLU calls its variant the cluster, and Modula the module. This discussion will retain the Ada term.)

在二十世紀七十年代,隨着信息隱藏和數據抽象概念的發展,需要一種比例程更高級的模塊形式.在當時的一些設計和編程語言中發現了這樣的結果CLU, Modula-2Ada是其中最好的.他們全都提供了一個相似的模塊形式,Ada中稱之爲包(或軟件包package).(CLU稱之爲變量羣集,Modula爲模塊.本討論中將會保留Ada的術語).

 

Packages are units of software decomposition with the following properties:

包是帶有下列各項特性的軟件分解單元:

 

P1 • In accordance with the Linguistic Modular Units principle, “package” is a construct of the language, so that every package has a name and a clear syntactic scope.

P1,符合語言學的模塊單元原則,"包"是一種語言概念,因此每個包都有一個名字和一個清晰的語法作用域.

 

P2 • Each package definition contains a number of declarations of related elements, such as routines and variables, hereafter called the features of the package.

P2,每個包定義中包含了許多相關元素的聲明,像是例程和變量,這些稱之爲包的功能(features).

 

P3 • Every package can specify precise access rights governing the use of its features by other packages. In other words, the package mechanism supports information hiding.

P3,每個包能夠指定精確的存取權限,控制着其它的包使用其功能.換句話說,包機制支持信息隱藏.

 

P4 • In a compilable language (one that can be used for implementation, not just specification and design) it is possible to compile packages separately.

P4,在一個編譯語言中(可被用於實現,不只是用於規格和設計),可以對包單獨地編譯.

 

Thanks to P3, packages deserve to be seen as abstracted modules. Their major contribution is P2, answering the Routine Grouping requirement. A package may contain any number of related operations, such as table creation, insertion, searching and deletion. It is indeed not hard to see how a package solution would work for our example problem. Here — in a notation adapted from the one used in the rest of this book for object-oriented software — is the sketch of a package INTEGER_TABLE_HANDLING describing a particular implementation of tables of integers, through binary trees:

由於P3,包應該被視爲抽象的模塊.它們的主要貢獻是P2,對應了例程分組的需求. 一個包可以包含任何數量的相關運算,像是表的創建,插入,查尋和刪除.我們會很容易的理解包如何解決我們例子中的問題.這裏,使用了一段在本書中作爲面向對象軟件所採用符號,來描繪了包INTEGER_TABLE_HANDLING,它通過二進制樹描述了整數表一個特別的實現:

 

package INTEGER_TABLE_HANDLING feature

type INTBINTREE is

record

-- Description of representation of a binary tree, for example:

info: INTEGER

left, right: INTBINTREE

end

new: INTBINTREE is

-- Return a new INTBINTREE, properly initialized.

do ¼ end

has (t: INTBINTREE x: INTEGER): BOOLEAN is

-- Does x appear in t?

do ¼ Implementation of searching operation ¼ end

put (t: INTBINTREE x: INTEGER) is

-- Insert x into t.

do ¼ end

remove (t: INTBINTREE x: INTEGER) is

-- Remove x from t.

do ¼ end

end -- package INTEGER_TABLE_HANDLING

 

This package includes the declaration of a type (INTBINTREE), and a number of routines representing operations on objects of that type. In this case there is no need for variable declarations in the package (although the routines may have local variables).

這個包包括了一個類型(INTBINTREE)的聲明,和許多在這個類型對象上的運算例程.在這種情況下並不需要在包裏聲明變量(雖然例程可能有局部變量).

 

Client packages will now be able to manipulate tables by using the various features of INTEGER_TABLE_HANDLING. This assumes a syntactic convention allowing a client to use feature f from package P let us borrow the CLU notation: P$f. Typical extracts from a client of INTEGER_TABLE_HANDLING may be of the form:

戶端包能夠通過使用INTEGER_TABLE_HANDLING的各種功能操縱表.裏假設一個語法約定允許一個戶端使用來自包P的功能f讓我們借CLU的符號: P$f. 來自INTEGER_TABLE_HANDLING的一個戶端典型的用法可能是這種形式:

 

-- Auxiliary declarations:

x: INTEGER b: BOOLEAN

-- Declaration of t using a type defined in INTEGER_TABLE_HANDLING:

t: INTEGER_TABLE_HANDLING$INTBINTREE

-- Initialize t as a new table, created by function new of the package:

t := INTEGER_TABLE_HANDLING$new

-- Insert value of x into table, using procedure put from the package:

INTEGER_TABLE_HANDLING$put (t, x)

-- Assign True or False to b, depending on whether or not x appears in t

-- for the search, use function has from the package:

b := INTEGER_TABLE_HANDLING$has (t, x)

 

Note the need to invent two related names: one for the module, here INTEGER_TABLE_HANDLING, and one for its main data type, here INTBINTREE. One of the key steps towards object orientation will be to merge the two notions. But let us not anticipate.

需要注意的是二個相關的命名:一個是模塊命名,這裏是INTEGER_TABLE_HANDLING,另一個是爲了它的主要數據類型,INTBINTREE.一個面向對象的關鍵步驟是合併這兩個符號.但我們先不這麼做.

 

A less important problem is the tediousness of having to write the package name (here INTEGER_TABLE_HANDLING) repeatedly. Languages supporting packages solve this problem by providing various syntactic shortcuts, such as the following Ada-like form:

一個不太重要的問題是不得不不斷地寫包的名字(這裏是INTEGER_TABLE_HANDLING),這很令人厭煩.支持包的語言提供各種不同的語法捷徑來解決這個問題, 如下列像Ada一樣的形式:

 

with INTEGER_TABLE_HANDLING then

¼ Here has means INTEGER_TABLE_HANDLING$has, etc. ¼

end

 

Another obvious limitation of packages of the above form is their failure to deal with the Type Variation issue: the module as given is only useful for tables of integers. We will shortly see, however, how to correct this deficiency by making packages generic.

對於上述形式的包,另一個明顯的限制是它們無法處理類型變化的議題: 給定的模塊只能對整數表有效.然而,我們不久就會知道,該如何通過包的泛化來改正這個缺點.

 

The package mechanism provides information hiding by limiting clients’ rights on features. The client shown on the preceding page was able to declare one of its own variables using the type INTBINTREE from its supplier, and to call routines declared in that supplier but it has access neither to the internals of the type declaration (the record structure defining the implementation of tables) nor to the routine bodies (their do clauses). In addition, you can hide some features of the package (variables, types, routines) from clients, making them usable only within the text of the package.

通過在功能上限制客戶端的權限,包機制提供了信息隱藏.在前頁所示的客戶端能夠聲明一個它自己的變量,使用供應者所提供的類型INTBINTREE,並調用在其供應者中被聲明的例程;但是它既不對所聲明的類型(定義表實現的record結構)內部存取,也不存取例程本身(它們的do子句).除此之外,您能對客戶端隱藏包(變量,類型,例程)裏面的一些功能,使得它們只有在包的代碼內部中才可用.

 

Languages supporting the package notion differ somewhat in the details of their information hiding mechanism. In Ada, for example, the internal properties of a type such as INTBINTREE will be accessible to clients unless you declare the type as private.

對於信息隱藏機制的細節,那些支持包觀念的語言稍微會有些不同.例如,在Ada中,像是INTBINTREE這樣的類型的內部屬性對用戶端來說是可用的,除非您聲明類型是私有的(private).

 

Often, to enforce information hiding, encapsulation languages will invite you to declare a package in two parts, interface and implementation, relegating such secret elements as the details of a type declaration or the body of a routine to the implementation part. Such a policy, however, results in extra work for the authors of supplier modules, forcing them to duplicate feature header declarations. With a better understanding of Information Hiding we do not need any of this. More in later chapters.

爲了強調信息陰藏,通常封裝語言會讓您在接口和實現這二部份中聲明包,把類型聲明的細節或例程的本身這樣的祕密元素轉移至實現部分.然而,這樣的一個政策爲供應模塊的作者造成額外的負擔,強迫他們複製特性表頭的聲明.隨着對信息隱藏的深入理解,我們並不需要這些.在稍後的章節中會有更多的討論.

 

Packages: an assessment

有關評估

 

Compared to routines, the package mechanism brings a significant improvement to the modularization of software systems into abstracted modules. The possibility of gathering a number of features under one roof is useful for both supplier and client authors:

與例程相比,包機制通過抽象的模塊對軟件系統模塊化帶來了顯著地提升.把衆多特性聚集在一起的可能性對供應者和客戶端作者都是有用的:

 

• The author of a supplier module can keep in one place and compile together all the software elements relating to a given concept. This facilitates debugging and change. In contrast, with separate subroutines there is always a risk of forgetting to update some of the routines when you make a design or implementation change you might for example update new, put and has but forget remove.

模塊供應者能把所有涉及給定概念的軟件元素保存在同一個地方,並且一起編譯. 這有利於除錯和變化.如果不這樣的話,當您在作一些設計或實現上的變化時,因爲存在分離的子例程,總是會有忘記更新一些例程的風險;舉例來說, 您可能更新了new, puthas, 但是忘了remove.

 

• For client authors, it is obviously easier to find and use a set of related facilities if they are all in one place.

對客戶端作者而言,如果例程全都是在一個地方,明顯地能夠比較容易地找到,並且很容易地運用一系列相關的工具.

 

The advantage of packages over routines is particularly clear in cases such as our table example, where a package groups all the operations applying to a certain data structure.

在很多的情況中,包勝過例程的優勢特別明顯,如表的例子,在裏面一個包聚集了所有適用於某個數據結構運算.

 

But packages still do not provide a full solution to the issues of reusability. As noted, they address the Routine Grouping requirementbut they leave the others unanswered. In particular they offer no provision for factoring out commonality. You will have noted that INTEGER_TABLE_HANDLING, as sketched, relies on one specific choice of implementation, binary search trees. True, clients do not need to be concerned with this choice, thanks to information hiding. But a library of reusable components will need to provide modules for many different implementations. The resulting situation is easy to foresee: a typical package library will offer dozens of similar but never identical modules in a given area such as table management, with no way to take advantage of the commonality. To provide reusability to the clients, this technique sacrifices reusability on the suppliers’ side.

但是對於複用性議題,包仍然不能提供完整的解決方案.如上所示,它們解決了例程分組的需求;但是它們無法處理其餘的要求.特別是它們並沒有爲合併通用行爲提供準備.您已經注意到了做爲描繪略圖的INTEGER_TABLE_HANDLING,依賴一種特定的實現-二進制查詢樹的選擇.實際上,由於信息隱藏的原因,客戶端並不需要考慮這種選擇.但是由於許多不同的實現,一個可複用的組件庫需要提供給模塊.產生的情形很容易預見:像是表管理這種特定的領域裏,一個典型的包的庫會提供幾十個相似的但是從不同樣的模塊,沒有什麼方法可以利用共通性.爲了要提供複用性給客戶端,這種技術在供應者一方犧牲了複用性.

 

Even on the clients’ side, the situation is not completely satisfactory. Every use of a table by a client requires a declaration such as the above:

t: INTEGER_TABLE_HANDLING$INTBINTREE

forcing the client to choose a specific implementation. This defeats the Representation Independence requirement: client authors will have to know more about implementations of supplier notions than is conceptually necessary.

至於在客戶端的一方,情形也並不完全令人滿意.被客戶端使用的每一個表都需要一個如下的聲明:

t: INTEGER_TABLE_HANDLING$INTBINTREE

強迫客戶端選擇一個明確的實現.這違反了表示法獨立的需求:客戶端作者將不得不瞭解有關供應者想法的實現,這大大超出必須要了解的概念.

 

4.8 OVERLOADING AND GENERICITY

4.8 重載和泛型

 

Two techniques, overloading and genericity, offer candidate solutions in the effort to bring more flexibility to the mechanisms just described. Let us study what they can contribute.

重載和泛型,這二種技術比剛纔所描述的機制給候選的解決方案帶來了更多的靈活性.讓我們來學習它們所能提供的好處.

 

Syntactic overloading

語法

 

Overloading is the ability to attach more than one meaning to a name appearing in a program.

重載是指對於在程序中出現的一個名字付於超過一個以上的意義的能力.

 

The most common source of overloading is for variable names: in almost all languages, different variables may have the same name if they belong to different modules (or, in the Algol style of languages, different blocks within a module).

最通常的重載的來源是爲了變量名: 在幾乎所有的語言中,如果屬於不同的模塊(或者,在Algol風格的語言中,在一個模塊裏面的不同的區塊中),不同的變量可以有着相同的名字.

 

More relevant to this discussion is routine overloading, also known as operator overloading, which allows several routines to share the same name. This possibility is almost always available for arithmetic operators (hence the second name): the same notation, a + b, denotes various forms of addition depending on the types of a and b (integer, single-precision real, double-precision real). But most languages do not treat an operation such as "+" as a routine, and reserve it for predefined basic types — integer, real and the like. Starting with Algol 68, which allowed overloading the basic operators, several languages have extended the overloading facility beyond language built-ins to user-defined operations and ordinary routines.

更多相關地討論是例程重載(routine overloading),也即是運算符重載,其允許一些例程有相同的名字.這種可能性幾乎總是適用於算術運算符(第二個名字由此而來): 相同的符號,a+b,示了ab類型(整數,單精度實數,雙精度實數)所決定各種不同形式的加法.但是絕大多數的語言不把諸如+這樣的運算看做一個例程,而視爲預先定義好的基本類型的保留符,如整數,實數等等.從允許重載基本運算符的Algol 68開始,一些語言已經延伸了重載的使用範圍,從語言的內置定義到用戶自定義的運算和常規例程.

 

In Ada, for example, a package may contain several routines with the same name, as long as the signatures of these routines are different, where the signature of a routine is defined here by the number and types of its arguments. (The general notion of signature also includes the type of the results, if any, but Ada resolves overloading on the basis of the arguments only.) For example, a package could contain several square functions:

square (x: INTEGER): INTEGER is do ¼ end

square (x: REAL): REAL is do ¼ end

square (x: DOUBLE): DOUBLE is do ¼ end

square (x: COMPLEX): COMPLEX is do ¼ end

Then, in a particular call of the form square (y), the type of y will determine which version of the routine you mean.

舉例來說,Ada,一個包可以包含一些相同名字的例程,只要這些例程的標記式(signature)是不同的,在這裏,例程的標記式是由其參數的數目和類型所定義的.(如果可能的話,標記式的通用概念也包括結果的類型,但是Ada中的重載只以參數爲基礎.) 舉例來說,一個包可以包含一些平方函數:

square (x: INTEGER): INTEGER is do ¼ end

square (x: REAL): REAL is do ¼ end

square (x: DOUBLE): DOUBLE is do ¼ end

square (x: COMPLEX): COMPLEX is do ¼ end

然後,square (y)的一個特定調用中,y的類型將決定您想使用的例程的版本.

 

A package could similarly declare a number of search functions, all of the form

has (t: “SOME_TABLE_TYPE” x: ELEMENT) is do ¼ end

supporting various table implementations and differing by the actual type used in lieu of “SOME_TABLE_TYPE”. The type of the first actual argument, in any client’s call to has, suffices to determine which routine is intended.

一個包可以同樣定義許多的查詢函數,所有的形式

has (t: “SOME_TABLE_TYPE” x: ELEMENT) is do ¼ end

通過代替“SOME_TABLE_TYPE”實際使用的類型支持各種不同的表實現和差異. 在任何客戶端調用has,第一個實參的類型足以決定使用哪個例程.

 

These observations suggest a general characterization of routine overloading, which will be useful when we later want to contrast this facility with genericity:

這些實例暗示了例程重載的一個總的特性,當我們稍後用泛型對比這個技術時其會非常有用.

Role of overloading

Routine overloading is a facility for clients. It makes it possible to write the same client text when using different implementations of a certain concept.

例程重載是一種用戶端的技術.當使用一項特定概念的不同實現的時候,它使編寫相同的客戶端代碼成爲可能.

 

 

 



 

What does routine overloading really bring to our quest for reusability? Not much. It is a syntactic facility, relieving developers from having to invent different names for various implementations of an operation and, in essence, placing that burden on the compiler. But this does not solve any of the key issues of reusability. In particular, overloading does nothing to address Representation Independence. When you write the call

has (t, x)

you must have declared t and so (even if information hiding protects you from worrying about the details of each variant of the search algorithm) you must know exactly what kind of table t is! The only contribution of overloading is that you can use the same name in all cases. Without overloading each implementation would require a different name, as in

has_binary_tree (t, x)

has_hash (t, x)

has_linked (t, x)

對複用性的探索,例程重載真正帶給我們了什麼?不是很多.它是語法的功能,基本上減輕了開發者必須爲一個運算的各種不同的實現發明不同的名字的負擔,並把這個負擔推給了編譯器.但是這並不能解決任何的複用性的關鍵議題.特別地,重載對錶示法獨立什麼也沒做.當您編寫調用has (t, x) 的時候,您必須先聲明t,而且因此(即使信息陰藏保護您免於爲查詢算法的每個變量的細節擔憂)您一定完全地清楚表t是什麼類型! 重載的唯一貢獻是您能在所有的情況下使用相同的名字而已.沒有重載的話,每個實現則會需要一個不同的名字,如

has_binary_tree (t, x)

has_hash (t, x)

has_linked (t, x)

 

Is the possibility of avoiding different names a benefit after all? Perhaps not. A basic rule of software construction, object-oriented or not, is the principle of non-deception: differences in semantics should be reflected by differences in the text of the software. This is essential to improve the understandability of software and minimize the risk of errors. If the has routines are different, giving them the same name may mislead a reader of the software into believing that they are the same. Better force a little more wordiness on the client (as with the above specific names) and remove any danger of confusion.

避免不同名字的可能性終究是一種優點嗎?也許不.面向對象或非面向對象的軟件構造的一個基本規則是非欺騙的原則(principle of nondeception): 語義學上的不同應該反映在軟件代碼上的不同.這對改良軟件的理解性和將錯誤的危險減到最少是很必要的.如果has例程是不同的,那麼給它們相同的名字也許會誤導軟件的一個讀者,使其相信它們並非不同.更好的方法是強迫在客戶端上多加上一些文字(如同上述的特定的名字)並能消除任何混亂的危險.

 

The further one looks into this style of overloading, the more limited it appears. The criterion used to disambiguate calls — the signature of argument lists — has no particular merit. It works in the above examples, where the various overloads of square and has are all of different signatures, but it is not difficult to think of many cases where the signatures would be the same. One of the simplest examples for overloading would seem to be, in a graphics system, a set of functions used to create new points, for example under the form

p1 := new_point (u, v)

更進一步研究重載的這種風格,就愈會發現它的侷限.過去一直用於消除調用歧義的標準-參數列表的標記式-沒有特別的價值.在上述的例子中它可以工作, 例子中squarehas的各種不同的重載有足夠不同的標記式,但是很容易想到的許多情況中標記式會是相同的.一個最簡單的重載例子是在圖形系統中一組產生新的點的函數,如下所示:

p1 := new_point (u, v)

 

There are two basic ways to specify a new point: through its cartesian coordinates x and y (the projections on the horizontal axis), and through its polar coordinates r and q (the distance to the origin, and the angle with the horizontal axis). But if we overload function new_point we are in trouble, since both versions will have the signature

new_point (p, q: REAL): POINT

這裏有兩個基本的方法來指定一個新的點: 通過它的笛卡兒座標xy(在水平軸上的投影), 和通過它的極座標rq (到原點的距離,和在水平軸上的角度). 但是如果我們重載new_point函數,那麼我們就會遇到麻煩,因爲這兩個版本都有相同的new_point (p, q: REAL): POINT標記式.

 

This example and many similar ones show that type signature, the criterion for disambiguating overloaded versions, is irrelevant. But no better one has been proposed.

這個例子和許多相似的情況都顯示了類型標記式並不是消除重載版本歧義的準則.但是卻沒有更好地提議.

 

The recent Java language regrettably includes the form of syntactic overloading just described, in particular to provide alternative ways to create objects.

遺憾的是,近來的JAVA語言包括了僅僅用來描述的重載語法的形式,而沒有特別提供其它可能的方法以產生對象.

 

Semantic overloading (a preview)

重載語義(概述)

 

The form of routine overloading described so far may be called syntactic overloading. The object-oriented method will bring a much more interesting technique, dynamic binding, which addresses the goal of Representation Independence. Dynamic binding may be called semantic overloading. With this technique, you will be able to write the equivalent of has (t, x), under a suitably adapted syntax, as a request to the machine that executes your software. The full meaning of the request is something like this:

到現在爲止,我們所描述的重載例程形式可以被稱之爲重載語法(syntactic overloading).面向對象的方法將會帶來更加有趣的技術,動態綁定,其實現了表示法獨立的目標.動態綁定可以稱之爲重載語義(semantic overloading).通過此技術,並藉助於一個對應的適當語法,您將能夠編寫has (t, x)的類似例程作爲一個在運行您軟件的機器上的請求.這個請求的完整意義有點像下列的描述:

 

Dear Hardware-Software Machine:

 

Please look at what t is I know that it must be a table, but not what table implementation its original creator chose — and to be honest about it I’d much rather remain in the dark. After all, my job is not table management but investment banking [or compiling, or computer-aided-design etc.]. The chief table manager here is someone else. So find out for yourself about it and, once you have the answer, look up the proper algorithm for has for that particular kind of table. Then apply that algorithm to determine whether x appears in t, and tell me the result. I am eagerly waiting for your answer.

 

I regret to inform you that, beyond the information that t is a table of some kind and x a potential element, you will not get any more help from me.

 

With my sincerest wishes,

 

Your friendly application developer.

 

親愛的硬件-軟件機器:

請考慮t是什麼;我知道它一定是一個表,但是不清楚表的最初創建者所選擇的實現.我寧可保持一無所知.畢竟,我的工作不是表的管理而是投資銀行業務[或編譯,或計算機輔助設計等等].在這裏,主要的表管理者是其他人.因此,請爲自己找出有關t的答案,一旦您找到了,對應於特定的表類型的has例程找出適當的算法.然後應用此算法去確定在t中是否有x,並且告訴我結果.我正在熱切地期待您的回答.

 

我很遺憾的告知您,除了t是某種類型的表和x是一個可能的元素這兩個信息, 您將不能從我這兒得到任何的幫助.

 

此致

敬禮

您忠實的應用開發者

 

Unlike syntactic overloading, such semantic overloading is a direct answer to the Representation Independence requirement. It still raises the specter of violating the principle of non-deception the answer will be to use assertions to characterize the common semantics of a routine that has many different variants (for example, the common properties which characterize has under all possible table implementations).

不同於重載語法,這種重載語義是表示法獨立需求的直接答案.它還引起了違犯非欺騙原則的擔心;對於一個具有許多不同變體的例程(例如,描述在所有可能的表實現之內的has的公共特性),答案是要使用斷言來描述其公共語義上的特性.

 

Because semantic overloading, to work properly, requires the full baggage of object orientation, in particular inheritance, it is understandable that non-O-O languages such as Ada offer syntactic overloading as a partial substitute in spite of the problems mentioned above. In an object-oriented language, however, providing syntactic overloading on top of dynamic binding can be confusing, as is illustrated by the case of C++ and Java which both allow a class to introduce several routines with the same name, leaving it to the compiler and the human reader to disambiguate calls.

因爲要使語義重載正確的工作,需要面向對象的全部理論,特別是繼承,像是Ada這樣的非OO語言提供語法重載作爲一個部分的替代,而不管上面所提到的問題, 這可以理解.然而,在面向對象的語言中,在動態綁定之前提供語法重載可能會使人困惑,如同C++和JAVA的情況所闡述的,兩者都允許一個類引入幾個有着相同名字的例程,讓編譯器和讀者(人)來區分其調用.

 

Genericity

泛型

 

Genericity is a mechanism for defining parameterized module patterns, whose parameters represent types.

泛型是定義參數化模塊模式的一個機制,它的參數描述了類型.

 

This facility is a direct answer to the Type Variation issue. It avoids the need for many modules such as

INTEGER_TABLE_HANDLING

ELECTRON_TABLE_HANDLING

ACCOUNT_TABLE_HANDLING

by enabling you instead to write a single module pattern of the form

TABLE_HANDLING [G]

where G is a name meant to represent an arbitrary type and known as a formal generic parameter. (We may later encounter the need for two or more generic parameters, but for the present discussion we may limit ourselves to one.)

這種功能是類型變化議題的直接回答.它通過讓您能夠改寫成一個單一模塊模式的形式TABLE_HANDLING [G] 避免了象下面這些模塊的需求

INTEGER_TABLE_HANDLING

ELECTRON_TABLE_HANDLING

ACCOUNT_TABLE_HANDLING

這裏,G是一個表達了一個任意的類名字,是一個泛化形參(formal generic parameter). (我們稍後可能遇到二更多的化形參的需,但是現在的討論我們限制爲一個.)

 

Such a parameterized module pattern is known as a generic module, although it is not really a module, only a blueprint for many possible modules. To obtain one of these actual modules, you must provide a type, known as an actual generic parameter, to replace G the resulting (non-generic) modules are written for example

TABLE_HANDLING [INTEGER]

TABLE_HANDLING [ELECTRON]

TABLE_HANDLING [ACCOUNT]

using types INTEGER, ELECTRON and ACCOUNT respectively as actual generic parameters. This process of obtaining an actual module from a generic module (that is to say, from a module pattern) by providing a type as actual generic parameter will be known as generic derivationthe module itself will be said to be generically derived.

這樣的一個參數化模塊模式即爲泛化模塊(generic module),雖然它並不是真正的一個模塊,只是許多可能模塊的一個藍圖.爲了要獲得這些真實的模塊,您必須要提供一個類型,即是一個泛化實參(actual generic parameter)來替代G結果的(非泛化的)模塊如下

TABLE_HANDLING [INTEGER]

TABLE_HANDLING [ELECTRON]

TABLE_HANDLING [ACCOUNT]

分別地使用INTEGER, ELECTRONACCOUNT類型作爲泛化實參.通過提供一個化實參的類型,從泛化模塊(也就是說,從一個模塊模式)獲得一個真正模塊的過程被視爲泛化派生(generic derivation)模塊的本身被稱之爲泛化派生模塊.

 

Two small points of terminology. First, generic derivation is sometimes called generic instantiation, a generically derived module then being called a generic instance. This terminology can cause confusion in an O-O context, since “instance” also denotes the run-time creation of objects (instances) from the corresponding types. So for genericity we will stick to the “derivation” terminology.

有兩個小的術語聲明.首先,泛化派生有時被稱之爲泛化實例(generic instantiation),一個泛化派生模塊被稱爲一個泛化實體(generic instance).由於"實體"也表示運行時創建的,來自對應類型的對象,這一種用辭能引起OO上下文的混亂,所以有關泛型我們會堅持使用"派生"術語.

 

Another possible source of confusion is “parameter”. A routine may have formal arguments, representing values which the routine’s clients will provide in each call. The literature commonly uses the term parameter (formal, actual) as a synonym for argument (formal, actual). There is nothing wrong in principle with either term, but if we have both routines and genericity we need a clear convention to avoid any misunderstanding. The convention will be to use “argument” for routines only, and “parameter” (usually in the form “generic parameter” for further clarification) for generic modules only.

另一個可能的混亂源頭是"參數(parameter)".一個例程可以有式自變量(formal arguments),描述例程的客戶端在每個調用中將會提供的數值.學術上普遍使用術語參數(parameter)(式的,實際的)作爲自變量(argument)的同義字(式的,實際的).對於任一個術語大體而言沒有什麼毛病,但是如果我們有例程和泛型,那麼我們需要一個清晰的約定以避免任何的歧義.對例程約定只使用自變量argument,泛化模塊只用參數parameter(爲了更清楚地表達,通常用泛化參數的形式).

 

Internally, the declaration of the generic module TABLE_HANDLING will resemble that of INTEGER_TABLE_HANDLING above, except that it uses G instead of INTEGER wherever it refers to the type of table elements. For example:

從裏面看, 泛化TABLE_HANDLING的聲明類似於上述的INTEGER_TABLE_HANDLING,除了使用G代替INTEGER用以指代表元素的類型.譬如:

 

package TABLE_HANDLING [G] feature

type BINARY_TREE is

record

info: G

left, right: BINARY_TREE

end

has (t: BINARY_TREE x: G): BOOLEAN

-- Does x appear in t?

do ¼ end

put (t: BINARY_TREE x: G) is

-- Insert x into t.

do ¼ end

(Etc.)

end -- package TABLE_HANDLING

 

It is somewhat disturbing to see the type being declared as BINARY_TREE, and tempting to make it generic as well (something like BINARY_TREE [G]). There is no obvious way to achieve this in a package approach. Object technology, however, will merge the notions of module and type, so the temptation will be automatically fulfilled. We will see this when we study how to integrate genericity into the object-oriented world.

看到類型被聲明爲BINARY_TREE是有點煩人,而且也誘惑您把它定義成泛型(有點象BINARY_TREE[G]). 在包的方式中並沒有顯而易見的方法來完成這些動作.然而,對象技術會合並模塊和類型的觀念,這樣,這些誘惑將會被自動地實現.當我們學習如何把泛型整合到面向對象領域的時候,我們就會了解了.

 

It is interesting to define genericity in direct contrast with the definition given earlier for overloading:

對比於先前給出的重載定義,定義泛型是有趣的:

Role of genericity

Genericity is a facility for the authors of supplier modules. It makes it possible to write the same supplier text when using the same implementation of a certain concept, applied to different kinds of object.

對於提供模快的作者來說,泛型是一個工具.當使用一個特定概念的相同實現的時候,它可以編寫相同的代碼應用到不同的類型對象上.

 



 

What help does genericity bring us towards realizing the goals of this chapter? Unlike syntactic overloading, genericity has a real contribution to make since as noted above it solves one of the main issues, Type Variation. The presentation of object technology in part C of this book will indeed devote a significant role to genericity.

對於實現本章的目標,泛型帶給我們什麼?不像重載語法,如上所述由於泛型解決一個主要的議題,類型變化,其有了一個真正的貢獻.本書的C部份中,對象技術的表示法將會貢獻一個重要的角色給於泛型.

 

Basic modularity techniques: an assessment

基本模塊技術評估

 

We have obtained two main results. One is the idea of providing a single syntactic home, such as the package construct, for a set of routines that all manipulate similar objects. The other is genericity, which yields a more flexible form of module.

我們已經得到了二個主要的結果.一是提供一個單一語法組的概念給一組操縱相似對象的例程,就像包的構造.另一個是泛型,它產生一個更靈活的模塊形式.

 

All this, however, only covers two of the reusability issues, Routine Grouping and Type Variation, and provides little help for the other three — Implementation Variation, Representation Independence and Factoring Out Common Behaviors. Genericity, in particular, does not suffice as a solution to the Factoring issue, since making a module generic defines two levels only: generic module patterns, parameterized and hence open to variation, but not directly usable and individual generic derivations, usable directly but closed to further variation. This does not allow us to capture the fine differences that may exist between competing representations of a given general concept.

然而,所有這些只包括了兩個複用性議題,例程分組和類型變化,對另外三個-實現變化,表示法獨立和合併通用行爲-卻沒什麼太大的幫助.特別地,泛型對於合併議題的解決方案並不足夠,這是由於使一個模塊泛化只定義二個層次: 泛化模塊模式,參數化和因此而來的對變化開放,但這並不直接有效;獨立的泛化派生,可直接應用但是對進一步的變化關閉.這不允許我們捕獲細微的差異,這些差異可能存在與一項特定的一般概念的表示法競爭之間.

 

On Representation Independence, we have made almost no progress. None of the techniques seen so far — except for the short glimpse that we had of semantic overloading — will allow a client to use various implementations of a general notion without knowing which implementation each case will select.

在表示法獨立上,我們幾乎止步不前.除了我們一帶而過的重載語義以外,迄今爲止沒有什麼技術會允許一個客戶端使用一個一般概念的各種不同的實現,而不必知道每種情況中將會選擇的實現.

 

To answer these concerns, we will have to turn to the full power of objectoriented concepts.

要解答這些關注的問題,我們將不得不轉向面向對象概念的強大威力.

 

4.9 KEY CONCEPTS INTRODUCED IN THIS CHAPTER

4.9 摘要

 

• Software development is a highly repetitive activity, involving frequent use of common patterns. But there is considerable variation in how these patterns are used and combined, defeating simplistic attempts to work from off-the-shelf components.

·        軟件開發是一個高度重複的活動,包括頻繁的使用通用模式.但是這些模式在如何使用和組合上有着相當的變化,這妨礙了應用現成組件的這種過分單純地嘗試.

 

• Putting reusability into practice raises economical, psychological and organizational problems the last category involves in particular building mechanisms to index, store and retrieve large numbers of reusable components. Even more important, however, are the underlying technical problems: commonly accepted notions of module are not adequate to support serious reusability.

·        實現複用性引發經濟上的,心理上的和組織上的問題;特別的,最終的範疇包括了索引,儲存和檢索大量可複用組件的構建機制.然而,更重要的是下列的技術問題: 有關模塊的普遍接受的概念並不能足以支持複雜的複用性.

 

• The major difficulty of reuse is the need to combine reuse with adaptation. The “reuse or redo” dilemma is not acceptable: a good solution must make it possible to retain some aspects of a reused module and adapt others.

·        複用的主要困難是結合複用和改寫的需要.複用或重做的選擇讓人爲難:一個好的解決方案必須使之可能,既保留一個被複用模塊的一些部分並改寫其它的部分.

 

• Simple approaches, such as reuse of personnel, reuse of designs, source code reuse, and subroutine libraries, have experienced some degree of success in specific contexts, but all fall short of providing the full potential benefits of reusability.

·        簡單的方法,像是人事的複用,設計的複用,源代碼的複用和子例程庫的複用,在特定的環境中已經經歷了一定程度地成功,但是卻無法提供複用性完整的可能的優勢.

 

• The appropriate unit of reuse is some form of abstracted module, providing an encapsulation of a certain functionality through a well-defined interface.

·        正確的複用單元是一些抽象模塊的形式,經過一個定義明確的接口提供具有一個特定功能性的封裝.

 

• Packages provide a better encapsulation technique than routines, as they gather a data structure and the associated operations.

·        由於聚集了一個數據結構和相關的運算,包提供了比例程更好的封裝技術.

 

• Two techniques extend the flexibility of packages: routine overloading, or the reuse of the same name for more than one operation genericity, or the availability of modules parameterized by types.

·        二種技術擴充了包的靈活性:例程重載,對多個運算的相同名字的複用;泛形, 類型參數化的模塊有效性.

 

• Routine overloading is a syntactic facility which does not solve the important issues of reuse, and harms the readability of software texts.

·        例程重載是一個語法工具,其不能解決複用的重要議題,而且傷害軟件代碼的可讀性.

 

• Genericity helps, but only deals with the issue of type variation.

·        泛形幫助而且解決了類型變化的議題.

 

• What we need: techniques for capturing commonalities within groups of related data structure implementations and techniques for isolating clients from having to know the choice of supplier variants.

·        什麼是我們所需的: 在相關的數據結構實現裏捕獲共通性的技術;從必須瞭解所提供的變體的選擇中隔離客戶端的技術.

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