“Follow the lead of hardware design! It is not right that every new development should start from scratch. There should be catalogs of software modules, as there are catalogs of VLSI devices: when we build a new system, we should be ordering components from these catalogs and combining them, rather than reinventing the wheel every time. We would write less software, and perhaps do a better job at that which we do get to write. Wouldn’t then some of the problems that everybody complains about — the high costs, the overruns, the lack of reliability — just go away? Why is it not so?”
You have probably heard remarks of this kind； perhaps you have uttered them yourself. As early as 1968, at the now famous NATO conference on software engineering, Doug McIlroy was advocating “mass-produced software components”. Reusability, as a dream, is not new.
您或许已经听到过这种评论；也许就是您自己说的.早在1968年,在那次著名的NATO(北大西洋公约组织)的软件工程会议上，Doug McIlroy就主张”大量生产软件组件(mass-produced software components)”.复用性,如同一个梦,早就存在了.
It would be absurd to deny that some reuse occurs in software development. In fact one of the most impressive developments in the industry since the first edition of this book was published in 1988 has been the gradual emergence of reusable components, often modest individually but regularly gaining ground； they range from small modules meant to work with Microsoft’s Visual Basic (VBX) and OLE 2 (OCX, now ActiveX) to full libraries, also known as “frameworks”, for object-oriented environments.
Another exciting development is the growth of the Internet: the advent of a wired society has eased or in some cases removed some of the logistic obstacles to reuse which, only a few years ago, might have appeared almost insurmountable.
But this is only a beginning. We are far from McIlroy’s vision of turning software development into a component-based industry. The techniques of object-oriented software construction make it possible for the first time to envision a state of the discipline, in the not too distant future, in which this vision will have become the reality, for the greatest benefit not just of software developers but, more importantly, of those who need their products — quickly, and at a high level of quality.
In this chapter we will explore some of the issues that must be addressed for reusability to succeed on such a large scale. The resulting concepts will guide the discussion of object-oriented techniques throughout the rest of this book.
4.1 THE GOALS OF REUSABILITY
We should first understand why it is so important to improve software reusability. No need here for “motherhood and apple pie” arguments: as we will see, the most commonly touted benefits are not necessarily the most significant； by going beyond the obvious we can make sure that our quest for reuse will pursue the right targets, avoid mirages, and yield the highest return on our investment.
From more reusable software you may expect improvements on the following fronts:
• Timeliness (in the sense defined in the discussion of software quality factors: speed of bringing projects to completion and products to market). By relying on existing components we have less software to develop and hence can build it faster.
时效性(在软件品质因数的讨论中已定义过: 项目完成和产品上市的速度). 通过使用现有的组件,我们的软件开发活动会变得更少并且因此构建会更快.
• Decreased maintenance effort. If someone else is responsible for the software, that someone is also responsible for its future evolutions. This avoids the competent developer’s paradox: the more you work, the more work you create for yourself as users of your products start asking you for new functionalities, ports to new platforms etc. (Other than relying on someone else to do the job, or retiring, the only solution to the competent software developer’s paradox is to become an incompetent developer so that no one is interested in your products any more — not a solution promoted by this book.)
• Reliability. By relying on components from a reputed source, you have the guarantee, or at least the expectation, that their authors will have applied all the required care, including extensive testing and other validation techniques； not to mention the expectation, in most cases, that many other application developers will have had the opportunity to try these components before you, and to come across any remaining bugs. The assumption here is not necessarily that the component developers are any smarter than you are； simply that the components they build — be they graphics modules, database interfaces, sorting algorithms ¼ — are their official assignment, whereas for you they might just be a necessary but secondary chore for the attainment of your official goal of building an application system in your own area of development.
• Efficiency. The same factors that favor reusability incite the component developers to use the best possible algorithms and data structures known in their field of specialization, whereas in a large application project you can hardly expect to have an expert on board for every field touched on by the development. (Most people, when they think of the connection between reusability and efficiency, tend to see the reverse effect: the loss of fine-tuned optimizations that results from using general solutions. But this is a narrow view of efficiency: in a large project, you cannot realistically perform such optimizations on every piece of the development. You can, however, aim at the best possible solutions in your group’s areas of excellence, and for the rest rely on someone else’s expertise.)
• Consistency. There is no good library without a strict emphasis on regular, coherent design. If you start using such a library — in particular some of the best current object-oriented libraries — its style will start to influence, through a natural process of osmosis, the style of the software that you develop. This is a great boost to the quality of the software produced by an application group.
• Investment. Making software reusable is a way to preserve the know-how and inventions of the best developers； to turn a fragile resource into a permanent asset.
Many people, when they accept reusability as desirable, think only of the first argument on this list, improving productivity. But it is not necessarily the most important contribution of a reuse-based software process. The reliability benefit, for example, is just as significant. It is extremely difficult to build guaranteeably reusable software if every new development must independently validate every single piece of a possibly huge construction. By relying on components produced, in each area, by the best experts around, we can at last hope to build systems that we trust, because instead of redoing what thousands have done before us — and, most likely, running again into the mistakes that they made — we will concentrate on enforcing the reliability of our truly new contributions.
This argument does not just apply to reliability. The comment on efficiency was based on the same reasoning. In this respect we can see reusability as standing apart from the other quality factors studied in chapter 1: by enhancing it you have the potential of enhancing almost all of the other qualities. The reason is economic: if, instead of being developed for just one project, a software element has the potential of serving again and again for many projects, it becomes economically attractive to submit it to the best possible quality-enhancing techniques — such as formal verification, usually too demanding to be cost-effective for most projects but the most mission-critical ones, or extensive optimization, which in ordinary circumstances can often be dismissed as undue perfectionism. For reusable components, the reasoning changes dramatically； improve just one element, and thousands of developments may benefit.
这个讨论不仅仅适用于可靠性.在效率方面的评论也以相同的论证为基础.基于这种考虑,我们可以认为复用性不同于第1章中所学的其它品质因数:通过加强复用性,您可以潜在的提高几乎所有的其它品质.动机是经济上的: 如果不仅仅考虑只为一个项目所开发,一个软件元素可能多次为许多项目服务,把它作为最佳的提高品质的技术, 这会使项目变得更具经济性－例如说形式验证(formal verification),对大多数的项目来说通常过于苛刻并不划算,但却是最关键性任务,再说广泛最佳化(extensive optimization),在通常的情况下由于过于追求尽善尽美常常被叫停.对于可复用的组件,结论却戏剧性地发生变化；只需改善一个元素,数以千计的开发得以受益.
This reasoning is of course not completely new； it is in part the transposition to software of ideas that have fundamentally affected other disciplines when they turned from individual craftsmanship to mass-production industry. A VLSI chip is more expensive to build than a run-of-the-mill special-purpose circuit, but if well done it will show up in countless systems and benefit their quality because of all the design work that went into it once and for all.
Reuse consumers, reuse producers
If you examined carefully the preceding list of arguments for reusability, you may have noted that it involves benefits of two kinds. The first four are benefits you will derive from basing your application developments on existing reusable components； the last one, from making your own software reusable. The next-to-last (consistency) is a little of both.
This distinction reflects the two aspects of reusability: the consumer view, enjoyed by application developers who can rely on components； and the producer view, available to groups that build reusability into their own developments.
这种区别反映了复用性的二个方面: 消费者观点(consumer view),被依赖于组件的应用程序开发者所喜爱；生产者观点(producer view),有利于团队在他们自己的开发中建立复用性.
In discussing reusability and reusability policies you should always make sure which one of these two views you have in mind. In particular, if your organization is new to reuse, remember that it is essentially impossible to start as a reuse producer. One often meets managers who think they can make development reusable overnight, and decree that no development shall henceforth be specific. (Often the injunction is to start developing “business objects” capturing the company’s application expertise, and ignore general-purpose components — algorithms, data structures, graphics, windowing and the like — since they are considered too “low-level” to yield the real benefits of reuse.) This is absurd: developing reusable components is a challenging discipline； the only known way to learn is to start by using, studying and imitating good existing components. Such an approach will yield immediate benefits as your developments will take advantage of these components, and it will start you, should you persist in your decision to become a producer too, on the right learning path.
Reuse Path principle Be a reuse consumer before you try to be a reuse producer. 在你试图成为一个复用的生产者之前,成为一个复用的消费者.
Reuse Path principle
Be a reuse consumer before you try to be a reuse producer.
4.2 WHAT SHOULD WE REUSE?
Convincing ourselves that Reusability Is Good was the easy part (although we needed to clarify what is really good about it). Now for the real challenge: how in the world are we going to get it?
The first question to ask is what exactly we should expect to reuse among the various levels that have been proposed and applied: reuse of personnel, of specifications, of designs, of “patterns”, of source code, of specified components, of abstracted modules.
Reuse of personnel
The most common source of reusability is the developers themselves. This form of reuse is widely practiced in the industry: by transferring software engineers from project to project, companies avoid losing know-how and ensure that previous experience benefits new developments.
This non-technical approach to reusability is obviously limited in scope, if only because of the high turnover in the software profession.
Reuse of designs and specifications
Occasionally you will encounter the argument that we should be reusing designs rather than actual software. The idea is that an organization should accumulate a repository of blueprints describing accepted design structures for the most common applications it develops. For example, a company that produces aircraft guidance systems will have a set of model designs summarizing its experience in this area； such documents describe module templates rather than actual modules.
This approach is essentially a more organized version of the previous one — reuse of know-how and experience. As the discussion of documentation has already suggested, the very notion of a design as an independent software product, having its own life separate from that of the corresponding implementation, seems dubious, since it is hard to guarantee that the design and the implementation will remain compatible throughout the evolution of a software system. So if you only reuse the design you run the risk of reusing incorrect or obsolete elements.
These comments are also applicable to a related form of reuse: reuse of specifications.
To a certain extent, one can view the progress of reusability in recent years, aided by progress in the spread of object technology and aiding it in return, as resulting in part from the downfall of the old idea, long popular in software engineering circles, that the only reuse worthy of interest is reuse of design and specification. A narrow form of that idea was the most effective obstacle to progress, since it meant that all attempts to build actual components could be dismissed as only addressing trivial needs and not touching the truly difficult aspects. It used to be the dominant view； then a combination of theoretical arguments (the arguments of object technology) and practical achievements (the appearance of successful reusable components) essentially managed to defeat it.
“Defeat” is perhaps too strong a term because, as often happens in such disputes, the result takes a little from both sides. The idea of reusing designs becomes much more interesting with an approach (such as the view of object technology developed in this book) which removes much of the gap between design and implementation. Then the difference between a module and a design for a module is one of degree, not of nature: a module design is simply a module of which some parts are not fully implemented； and a fully implemented module can also serve, thanks to abstraction tools, as a module design.With this approach the distinction between reusing modules (as discussed below) and reusing designs tends to fade away.
In the mid-nineteen-nineties the idea of design patterns started to attract considerable attention in object-oriented circles. Design patterns are architectural ideas applicable across a broad range of application domains； each pattern makes it possible to build a solution to a certain design issue.
Here is a typical example, discussed in detail in a later chapter. The issue: how to provide an interactive system with a mechanism enabling its users to undo a previously executed command if they decide it was not appropriate, and to reexecute an undone command if they change their mind again. The pattern: use a class COMMAND with a precise structure (which we will study) and an associated “history list”. We will encounter many other design patterns.
One of the reasons for the success of the design pattern idea is that it was more than an idea: the book that introduced the concept, and others that have followed, came with a catalog of directly applicable patterns which readers could learn and apply.
Design patterns have already made an important contribution to the development of object technology, and as new ones continue to be published they will help developers to benefit from the experience of their elders and peers. How can the general idea contribute to reuse? Design patterns should not encourage a throwback to the “all that counts is design reuse” attitude mentioned earlier. A pattern that is only a book pattern, however elegant and general, is a pedagogical tool, not a reuse tool； after all, computing science students have for three decades been learning from their textbooks about relational query optimization, Gouraud shading, AVL trees, Hoare’s Quicksort and Dijkstra’s shortest path algorithm without anyone claiming that these techniques were breakthroughs in reusability. In a sense, the patterns developed in the past few years are only incremental additions to the software professional’s bag of standard tricks. In this view the new contribution is the patterns themselves, not the idea of pattern.
As most people who have looked carefully at the pattern work have recognized, such a view is too limited. There seems to be in the very notion of pattern a truly new contribution, even if it has not been fully understood yet. To go beyond their mere pedagogical value, patterns must go further. A successful pattern cannot just be a book description: it must be a software component, or a set of components. This goal may seem remote at first because many of the patterns are so general and abstract as to seem impossible to capture in actual software modules； but here the object-oriented method provides a radical contribution. Unlike earlier approaches, it will enable us to build reusable modules that still have replaceable, not completely frozen elements: modules that serve as general schemes (patterns is indeed the appropriate word) and can be adapted to various specific situations. This is the notion of behavior class (a more picturesque term is programs with holes)； it is based on O-O techniques that we will study in later chapters, in particular the notion of deferred class. Combine this with the idea of groups of components intended to work together — often known as frameworks or more simply as libraries — and you get a remarkable way of reconciling reusability with adaptability. These techniques hold, for the pattern movement, the promise of exerting, beyond the new-bag-of-important-tricks effect, an in-depth influence on reusability practices.
大多数在仔细研究模式工作的人已经认识到,这样的一个观点太有局限性.在模式的真正观念里面似乎形成了一个真实的新贡献,即使它仍然没有被完全地了解.为了要超越它们纯粹的教学价值,模式一定要更进一步.一个成功的模式不能够仅仅是书本上的描述:它一定是一个软件组件,或一个组件组. 因为许多模式过于通用和抽象,似乎不太可能在真正的软件模块中得到,所以这一个目标起先看上去有些遥不可及；但在这里,面向对象的方法提供一个根本的帮助.不同于早期的方法,它将使我们能够构建可复用的模块,这些模块是可替换的,并没完全地固定其元素:模块可以适用于通用方案(模式的确是适当的单词),也能适应各种不同的特定情形.这是行为类(Behavior Class)的概念(一个较生动的术语是有洞的程序(programs with holes))；它是基于将会在后面的章节中学习到的OO技术之上,特别是延期类的观念.把它和组件组的思想结合起来共同工作－即是框架或更简单的称为库－同时您能获得适应性和复用性相一致的显著方法.为了模式发展,这些技术在复用性实践上产生着深入的影响,并超过了新智囊的效果.
Reusability through the source code
Personnel, design and specification forms of reuse, useful as they may be, ignore a key goal of reusability. If we are to come up with the software equivalent of the reusable parts of older engineering disciplines, what we need to reuse is the actual stuff of which our products are made: executable software. None of the targets of reuse seen so far — people, designs, specifications — can qualify as the off-the-shelf components ready to be included in a new software product under development.
If what we need to reuse is software, in what form should we reuse it? The most natural answer is to use the software in its original form: source text. This approach has worked very well in some cases. Much of the Unix culture, for example, originally spread in universities and laboratories thanks to the on-line availability of the source code, enabling users to study, imitate and extend the system. This is also true of the Lisp world.
如果我们需要复用的是软件,我们应该复用它的什么形式? 最自然的回答是使用软件的最初形式: 源代码. 这种方式已经在一些情况中工作得很好.举例来说，许多的Unix系统的培养,最初都在大学和实验室里传播,这是因为可以在线使用源代码,使得用户能够学习,模仿并且扩充系统.Lisp领域也是一样的.
The economic and psychological impediments to source code dissemination limit the effect that this form of reuse can have in more traditional industrial environments. But a more serious limitation comes from two technical obstacles:
• Identifying reusable software with reusable source removes information hiding. Yet no large-scale reuse is possible without a systematic effort to protect reusers from having to know the myriad details of reused elements.
• Developers of software distributed in source form may be tempted to violate modularity rules. Some parts may depend on others in a non-obvious way, violating the careful limitations which the discussion of modularity in the previous chapter imposed on inter-module communication. This often makes it difficult to reuse some elements of a complex system without having to reuse everything else.
A satisfactory form of reuse must remove these obstacles by supporting abstraction and providing a finer grain of reuse.
Reuse of abstracted modules
All the preceding approaches, although of limited applicability, highlight important aspects of the reusability problem:
• Personnel reusability is necessary if not sufficient. The best reusable components are useless without well-trained developers, who have acquired sufficient experience to recognize a situation in which existing components may provide help.
• Design reusability emphasizes the need for reusable components to be of sufficiently high conceptual level and generality — not just ready-made solutions to special problems. The classes which we will encounter in object technology may be viewed as design modules as well as implementation modules.
• Source code reusability serves as a reminder that software is in the end defined by program texts. A successful reusability policy must produce reusable program elements.
The discussion of source code reusability also helps narrow down our search for the proper units of reuse. A basic reusable component should be a software element. (From there we can of course go to collections of software elements.) That element should be a module of reasonable size, satisfying the modularity requirements of the previous chapter； in particular, its relations to other software, if any, should be severely limited to facilitate independent reuse. The information describing the module’s capabilities, and serving as primary documentation for reusers or prospective reusers, should be abstract: rather than describing all the details of the module (as with source code), it should, in accordance with the principle of Information Hiding, highlight the properties relevant to clients.
源代码复用性的讨论也帮助我们缩小了搜寻适当的复用单元的范围.一个基本的可复用组件应该是一个软件元素.(当然从那里我们能定位到软件元素的集合.) 那个元素应该是一个合理大小的模块,满足先前章节所说的模块化需求；特别地,和其它软件的关系应该被严格地限制,使其有利于独立复用.描述模块能力的信息,和服务于复用者或准复用者的主要文档,应该是抽象的: 胜于描述模块(或源代码)的所有细节,它应该符合信息隐藏的原则,强调与客户端相关的属性.
The term abstracted module will serve as a name for such units of reuse, consisting of directly usable software, available to the outside world through a description which contains only a subset of each unit’s properties.
The rest of part B of this book is devoted to devising the precise form of such abstracted modules； part C will then explore their properties.
The emphasis on abstraction, and the rejection of source code as the vehicle for reuse, do not necessarily prohibit distributing modules in source form. The contradiction is only apparent: what is at stake in the present discussion is not how we will deliver modules to their reusers, but what they will use as the primary source of information about them. It may be acceptable for a module to be distributed in source form but reused on the basis of an abstract interface description.
着重于抽象化,并拒绝源代码作为复用的载体,这不必禁止源代码形式的分布式模块. 矛盾只是表面的: 在目前讨论中危险的不是我们将如何发送模块给它们的复用者,而是他们将使用模块作为主要的的信息资源.以源代码的形式分发但以一个抽象接口描述为基础复用,对一个模块来说这是可接受的。
To progress in our search for the ideal abstracted module, we should take a closer look at the nature of software construction, to understand what in software is most subject to reuse.
Anyone who observes software development cannot but be impressed by its repetitive nature. Over and again, programmers weave a number of basic patterns: sorting, searching, reading, writing, comparing, traversing, allocating, synchronizing¼
Experienced developers know this feeling of déjà vu, so characteristic of their trade.
观察软件开发的任何人都必然对它的重复性质留下深刻的印象.程序员们反复地编排着大量的基本模式:排序,查找,读,写,比较,遍历,分配,同步… 富有经验的开发者了解déjà vu的感觉,这在他们的职业中非常的典型.
[注]: déjà vu 是一个强大的,自定义的CAT(Computer Aided Translation)系统.
A good way to assess this situation (assuming you develop software, or direct people who do) is to answer the following question:
How many times over the past six months did you, or people working for you, write some program fragment for table searching? 在过去六个月以来,您或您的手下,为表查询写了多少段程序?
How many times over the past six months did you, or people working for you, write some program fragment for table searching?
Table searching is defined here as the problem of finding out whether a certain element x appears in a table t of similar elements. The problem has many variants, depending on the element types, the data structure representation for t, the choice of searching algorithm.
Chances are you or your colleagues will indeed have tackled this problem one or more times. But what is truly remarkable is that — if you are like others in the profession — the program fragment handling the search operation will have been written at the lowest reasonable level of abstraction: by writing code in some programming language, rather than calling existing routines.
To an observer from outside our field, however, table searching would seem an obvious target for widely available reusable components. It is one of the most researched areas of computing science, the subject of hundreds of articles, and many books starting with volume 3 of Knuth’s famous treatise. The undergraduate curriculum of all computing science departments covers the most important algorithms and data structures. Certainly not a mysterious topic. In addition:
[注]: <<The Art of Computer Porgramming, Volume 3 – Sorting and Searchng Second Edition>>
• It is hardly possible, as noted, to write a useful software system which does not include one or (usually) several cases of table searching. The investment needed to produce reusable modules is not hard to justify.
• As will be seen in more detail below, most searching algorithms follow a common pattern, providing what would seem to be an ideal basis for a reusable solution.