# 面向对象软件构造(第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.

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.

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.)

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.)

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:

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).

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.

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:

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 重载和泛型

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.

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).

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.

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.

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

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

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 overloadingRoutine 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_binary_tree (t, x)

has_hash (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.

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)

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

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.

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:

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.

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).

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.

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.)

INTEGER_TABLE_HANDLING

## ELECTRON_TABLE_HANDLING

ACCOUNT_TABLE_HANDLING

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.

TABLE_HANDLING [INTEGER]

TABLE_HANDLING [ELECTRON]

TABLE_HANDLING [ACCOUNT]

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.

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.

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:

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.

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

 Role of genericityGenericity 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.

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.

·        什么是我们所需的: 在相关的数据结构实现里捕获共通性的技术；从必须了解所提供的变体的选择中隔离客户端的技术.

• 评论

• 上一篇
• 下一篇