Figure 1 -- Historical lifecycle change costs.
Figure 2 -- Comtemporary lifecycle change costs.
The Cost of Change
变化的代价
Early on in Beck's book, he challenges one of the oldest assumptions in software engineering. From the mid-1970s, structured methods and then more comprehensive methodologies were sold based on the "facts" shown in Figure 1. I should know; I developed, taught, sold, and installed several of these methodologies during the 1980s.
Beck从他的早期的著作开始,就不断向那些软件工程中的一些"古训"发出挑战。从19世纪70年代中期的结构化方法,以至后来的那些更复杂的方法,他们都基于如图1所示的那个"事实",在整个80年代,我必须了解、使用、讨论、实施这些方法。
Beck asks us to consider that perhaps the economics of Figure 1, probably valid in the 1970s and 1980s, now look like Figure 2 - that is, the cost of maintenance, or ongoing change, flattens out rather than escalates. Actually, whether Figure 2 shows today's cost profile or not is irrelevant -- we have to make it true! If Figure 1 remains true, then we are doomed because of today's pace of change.
Beck却给我们提了一个问题,那些在70年代和80年代也许还能起到效果的方法,他们的经费开销状况(如图1)现在已经发生了变化(如图2),也就是说,维护的成本(也可以等价为不断发生的变化)降低了,而不是越来越高。实际上,图2所示的开销情况在当今是否是事实其实并不重要,重要的是我们必须认识到,如果图1的现象还在继续重演的话,我们只有死路一条,因为当今时代变化实在太快了(也就是说维护的成本将是一个天价)。
The vertical axis in Figure 1 usually depicts the cost of finding defects late in the development cycle. However, this assumes that all changes are the results of a mistake -- i.e., a defect. Viewed from this perspective, traditional methods have concentrated on "defect prevention" in early lifecycle stages. But in today's environment, we can't prevent what we don't know about -- changes arise from iteratively gaining knowledge about the application, not from a defective process. So, although our practices need to be geared toward preventing some defects, they must also be geared toward reducing the cost of continuous change. Actually, as Alistair Cockburn points out, the high cost of removing defects shown by Figure 1 provides an economic justification for practices like pair programming.
图1中的y轴通常用来表示在开发周期的后期发现错误后需要花费的改错成本。可是,这正验证了一个假设,即后期所有需要做的开动均来自前期的一个错误,比方说一个设计缺陷。从这一点来看,传统方法太依赖于在软件生命周期的早期"不出错"。但是在当今瞬息万变的环境中,我们不能完全预防住那些我们预测不到的东西--即由应用需求不断增长而带来的变化,并且这种变化在早期不可能遇见并加以预防。因此,虽然我们要尽可能在早期做出某些应付变化的预防措施,但是更重要的是我们要减少后期改变所带来的开销。正如 Alistai Cockburn 所指出的,需要高成本的图1所示的那种改正缺陷方法,正好从节省开支的角度给了一些实用的方法(如配对编程)一个好的理由。
In this issue of eAD, I want to restrict the discussion to change at the project or application level -- decisions about operating systems, development language, database, middleware, etc., are constraints outside the control of the development team. (For ideas on "architectural" flexibility, see the June and July 1999 issues of ADS.) Let's simplify even further and assume, for now, that the business and operational requirements are known.
在本期eAD杂志中,我打算把讨论定位于项目或应用软件层次上的变化--对类似操作系统、编程语言、数据库、组件等的讨论不在讨论之列。(关于软件结构的灵活性,可以参考ADS杂志1999年6月的那期)另外,让我们进一步做个简化,即假定软件的用户需求已经确定。
Our design goal is to balance the rapid delivery of functionality while we also create a design that can be easily modified. Even within the goal of rapid delivery, there remains another balance: proceed too hurriedly and bugs creep in; try to anticipate every eventuality and time flies. However, let's again simplify our problem and assume we have reached a reasonable balance of design versus code and test time.
我们的目标是既能快速不断的发布新功能,同时又要让软件的设计易于更改。即使是在快速发布这个目标下,仍然需要在"快速发布但Bug丛生"和"面面俱到但旷日持久"之间进行取舍。因此,让我再简化一下我们要讨论的问题,我们假定我们已经在设计、编码和测试这三者之间取得了合理的平衡。
With all these simplifications, we are left with one question: how much anticipatory design work do we do? Current design produces the functionality we have already specified. Anticipatory design builds in extra facilities with the anticipation that future requirements will be faster to implement. Anticipatory design trades current time for future time, under the assumption that a little time now will save more time later. But under what conditions is that assumption true? Might it not be faster to redesign later, when we know exactly what the changes are, rather than guessing now?
在上面这些简化的基础上,还留有一个尾巴:我们在设计时对于未知的未来要看多远?现在的设计已经实现了我们现在想到的一些功能。具有预见性的设计可以使未来的需求更快的获得实现,也就是说预见性设计方法在以现在的时间换取未来的时间,如果一点点现在的时间可以换来未来节约大量时间,当然是划算的。但是这种建设怎么才能成为现实呢?也许未来出了问题就整个重新设计一遍也不慢,那又何必现在瞎猜呢?
This is where refactoring enters the equation. Refactoring, according to author Martin Fowler, is "the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure." XP proponents practice continuous, incremental refactoring as a way to incorporate change. If changes are continuous, then we'll never get an up-front design completed. Furthermore, as changes become more unpredictable -- a great likelihood today -- then much anticipatory design likely will be wasted.
这就是我们为什么要提出重构的原因。重构,Martin Fowler说过,是不改变软件对外表现但是重整内务的一种改进。XP方法的支持者在变化的环境中实践了连续的、增量式的重构方法。如果变化是不断演化的的,那就不可能存在什么一步到位的设计方法。说白了,如果变化不可预测--正如当今社会的情况--过多的在设计时考虑以后可能的变化,完全是一种浪费。
Figure 3 -- Balancing design and refactoring, pre-internet.
Figure 4 -- Balancing design and refactoring today.
I think the diagram in Figure 3 depicts the situation prior to the rapid-paced change of the Internet era. Since the rate of change (illustrated by the positioning of the balance point in the figure) was lower, more anticipatory designing versus refactoring may have been reasonable. As Figure 4 shows, however, as the rate of change increases, the viability of anticipatory design loses out to refactoring- - a situation I think defines many systems today.
我认为图3给出的是互联网时代到来之前的情况。由于变化的速度慢(图中由天平的支点比较靠左来表示),早期的预测多一些是合理的。但是在图4中,由于变化速度变快乐,设计时预测太多是得不偿失的,这种情况正是现在许多系统所面临的。
In the long run, the only way to test whether a design is flexible involves making changes and measuring how easy they are to implement. One of the biggest problems with the traditional up- front-design-then-maintain strategy has been that software systems exhibit tremendous entropy; they degrade over time as maintainers rush fixes, patches, and enhancements into production. The problem is worse today because of the accelerated pace of change, but current refactoring approaches aren't the first to address the problem. Back in the "dark ages" (circa 1986), Dave Higgins wrote Data Structured Software Maintenance, a book that addressed the high cost of maintenance, due in large part to the cumulative effects of changes to systems over time. Although Higgins advocated a particular program-design approach (the Warnier/Orr Approach), one of his primary themes was to stop the degradation of systems over time by systematically redesigning programs during maintenance activities.
在一个长期项目中,检验一个设计是否具有很好的灵活性是通过变化需求,同时看看原设计能否很容易的实现新变化的需求。这种传统的"先设计,再维护"策略的最大问题在于软件系统存在非常大的熵(极易变化,没有规律)。一个系统随着时间的推移,维护、改错、打补丁、增强功能等工作会使系统的熵越来越大。现在由于外部环境变化加快,情况正越来越糟。不过,现在的重构技术也不是第一个试图解决这个问题的方法。早在所谓的"黑暗时期"(circa 1986),Dave Higgins 就写过一本名为"Data Structured Software Maintenance"的书,该书指出了由于随着时间的推移变化的累计影响不断增大,维护所需要的开销也将越来说庞大,Higgins 提出了一种新的设计方法(the Warnier/Orr Approach)用于阻止系统的熵增大所带来的负面影响,该方法的思想是在维护过程中有系统的对程序进行重新设计。
Higgins's approach to program maintenance was first to develop a pattern (although the term pattern was not used then) for how the program "should be" designed, then to create a map from the "good" pattern to the "spaghetti" code. Programmers would then use the map to help understand the program and, further, to revise the program over time to look more like the pattern. Using Higgins's approach, program maintenance counteracted the natural tendency of applications to degrade over time. "The objective was not to rewrite the entire application," said Higgins in a recent conversation, "but to rewrite those portions for which enhancements had been requested."
Higgins 的方法首先为程序改如何设计设定一种模式(虽然那时还没有模式这个提法),然后在细致的代码设计与"好"的模式之间建立一种映射,程序员即根据这种映射关系来理解系统并修改程序,使修改的结果更接近于那个模式。使用 Higgins 这个方法可以通过维护抵消系统谁时间而熵增大的趋势。Higgins 说:"该方法的目标并不是重写整个系统,而只是重写那些根据需要必须增强的部分。"
Although this older-style "refactoring" was not widely practiced, the ideas are the same as they are today -- the need today is just greater. Two things enable, or drive, increased levels of refactoring: one is better languages and tools, and the other is rapid change.
虽然这种原始的"重构"技术并没有被广泛的实践检验,其思想与现在的重构还是相通的,只不过现在的需求变化更快、更大。不过有两个东西驱动、提高了现代的重构技术:一是更好的程序设计语言和开发工具;二是更快的变化需求。
Another approach to high change arose in the early days of RAD: the idea of throwaway code. The idea was that things were changing so rapidly that we could just code applications very quickly, then throw them away and start over when the time for change arose. This turned out to be a poor long-term strategy.
在早期的 RAD(快速原型开发)方法中还有另一种应付变化的办法:代码抛弃思想。这个思想认为环境和需求变化太快,因此我们唯一的办法只能是快速编写新代码,并且也快速的抛弃老代码。我们认为这不是长久之计。
Refactoring
重构
Refactoring is closely related to factoring, or what is now referred to as using design patterns. Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, provides the foundational work on design patterns. Design Patterns serves modern-day OO programmers much as Larry Constantine and Ed Yourdon's Structural Design served a previous generation; it provides guidelines for program structures that are more effective than other program structures.
重构(Refactoring)与构造 (factoring),或者说与设计模式的使用密切相关。Erich Gamma, Richard Helm, Ralph Johnson, 和 John Vlissides合著的《 Design Patterns: Elements of Reusable Object-Oriented 》一书为设计模式做出了奠基性的工作。正如Larry Constantine 和Ed Yourdon 所倡导的结构化设计一样,设计模式对当代的面向对象技术程序设计做出了巨大的贡献,为开发人员带来了福音。通过设计模式,程序的结构的比以往更为有效。
If Figure 4 shows the correct balance of designing versus refactoring for environments experiencing high rates of change, then the quality of initial design remains extremely important. Design patterns provide the means for improving the quality of initial designs by offering models that have proven effective in the past.
如果图表4 所显示的设计(designing)与重构(refactoring)在面对高速变化环境时的适应能力方面的差别是客观的话,初始设计的质量则显的尤为重要。通过提供过去已被证明是有效的模式,设计模式(Design patterns)提供了一种提高初始设计质量的方法。
So, you might ask, why a separate refactoring book? Can't we just use the design patterns in redesign? Yes and no. As all developers (and their managers) understand, messing with existing code can be a ticklish proposition. The cliché "if it ain't broke, don't fix it" lives on in annals of development folklore. However, as Fowler comments, "The program may not be broken, but it does hurt." Fear of breaking some part of the code base that's "working" actually hastens the degradation of that code base. However, Fowler is well aware of the concern: "Before I do the refactoring, I need to figure out how to do it safely.... I've written down the safe steps in the catalog." Fowler's book, Refactoring: Improving the Design of Existing Code, catalogs not only the before (poor code) and after (better code based on patterns), but also the steps required to migrate from one to the other. These migration steps reduce the chances of introducing defects during the refactoring effort.
现在,也许你会问,为什么还需要一本独立讲重构的书呢?难道我们不可以只使用设计模式来重新设计吗?可以,也不可以。正如所有的开发人员(包括管理者)都知道,修改原有的程序代码是一件棘手的事。development folklore年刊上有一句话,"if it ain't broke,don't fix it".然而,正如Fowler所提到的,"程序也许还没有'坏掉',但却造成了潜在的危害." 害怕对那些还能"工作"的代码重新构造实际上只会加剧代码性能的衰退。同时,Fowler也清楚的认识到:"在软件重构之前,需要找到安全的做法……我把这些安全的步骤写进了目录"。Fowler所写的<>,不仅编录了如何对以前的(差的)代码和以后的(基于模式设计的较好)的代码进行重构的方法,而且也包含了代码分割重构的步骤。这些步骤减少了在重构过程中出现差错的机会。
Beck describes his "two-hat" approach to refactoring -- namely that adding new functionality and refactoring are two different activities. Refactoring, per se, doesn't change the observable behavior of the software; it enhances the internal structure. When new functionality needs to be added, the first step is often to refactor in order to simplify the addition of new functionality. This new functionality that is proposed, in fact, should provide the impetus to refactor.
Beck用"two-hat"方法来描述重构,也就是说, 添加新的功能与重构是两种不同的行为。在本质上,重构不改变软件可见的外部功能,它只是增强了软件的内部结构。当有新的功能需要添加时,第一步常是对软件进行重构,使添加更简化。事实上,这种添加的新功能为重构提供着推动力。
Refactoring might be thought of as incremental, as opposed to monumental, redesign. "Without refactoring, the design of the program will decay," Fowler writes. "Loss of structure has a cumulative effect." Historically, our approach to maintenance has been "quick and dirty," so even in those cases where good initial design work was done, it degraded over time.
与重量级的再设计相反,重构可以被认为是增量(incremental)式的再设计,"没有重构,程序设计会 腐烂",Fowler写到," 结构性的缺陷会带来累积效应 "。历史上,我们对软件维护的方法是"quick and dirty"(快速但不彻底的?),致使一些初始设计工作做的好的项目,随着时间推移,也会"退化"(degrade).
Figure 5 -- Software entropy over time.
Figure 5 shows the impact of neglected refactoring -- at some point, the cost of enhancements becomes prohibitive because the software is so shaky. At this point, monumental redesign (or replacement) becomes the only option, and these are usually high- risk, or at least high-cost, projects. Figure 5 also shows that while in the 1980s software decay might have taken a decade, the rate of change today hastens the decay. For example, many client- server applications hurriedly built in the early 1990s are now more costly to maintain than mainframe legacy applications built in the 1980s.
图表 5 显示了没有重构时的情况:因为软件是如此的不可靠,升级维护费用变的让人望而却步,于是巨型(monumental)设计(或替换)成了唯一选择,项目的风险,至少是投入上,变的越来越大。图 5也显示,在80年代,软件的生存期大约要10年,而在今天需求的快速变化加剧了软件的腐烂。举个例子,许多90年代初一窝蜂做出来的C/S应用软件在今天比80年代留下来的大型机软件的维护费用还要高的多。
Data Refactoring: Comments by Ken Orr
数据重构: Ken Orr注解
Editor's Note: As I mentioned above, one thing I like about XP and refactoring proponents is that they are clear about the boundary conditions for which they consider their ideas applicable. For example, Fowler has an entire chapter titled "Problems with Refactoring." Database refactoring tops Fowler's list. Fowler's target, as stated in the subtitle to his book, is to improve code. So, for data, I turn to someone who has been thinking about data refactoring for a long time (although not using that specific term). The following section on data refactoring was written by Ken Orr.
编者注: 如上所提,XP和重构思想吸引我的一点是他们能够清楚认识到所要考虑实施问题的边界条件(boundary conditions).例如,Fowler写了一章"Problems with Refactoring".其中首要的问题就是数据库的重构。正如书的副标题所示,Fowler的目标是为了提高代码质量。为此,我咨询了一些在数据重构(或者用其他的术语)方面有较深研究的人。以下关于数据重构部分由Ken Orr所写。
When Jim asked me to put together something on refactoring, I had to ask him what that really meant. It seemed to me to come down to a couple of very simple ideas:
当Jim 要我讲一讲重构时,我问他重构究竟意味着什么。对我来说,把它归纳为以下简单的几点:
Do what you know how to do.
做你会做的
Do it quickly.
速战速决
When changes occur, go back and redesign them in.
当发生变化时,回过头来重新设计
Go to 1.
回到 1
Over the years, Jim and I have worked together on a variety of systems methodologies, all of which were consistent with the refactoring philosophy. Back in the 1970s, we created a methodology built on data structures. The idea was that if you knew what people wanted, you could work backward and design a database that would give you just the data that you needed, and from there you could determine just what inputs you needed to update the database so that you could produce the output required.
在过去几年中,Jim和我一起工作,共同研究各种系统方法学(systems methodologies),发现所有的方法学与重构思想(refactoring philosophy)有着一致的地方。70年代,我们建立了一种基于数据结构的方法学。其主要思想是:在知道了人们的需求后,逆向工作,设计一个仅含必需数据的数据库,然后再确定更新数据库必需的输入数据,产生需要的输出数据。
Creating systems by working backward from outputs to database to inputs proved to be a very effective and efficient means of developing systems. This methodology was developed at about the same time that relational databases were coming into vogue, and we could show that our approach would always create a well-behaved, normalized database. More than that, however, was the idea that approaching systems this way created minimal systems. In fact, one of our customers actually used this methodology to rebuild a system that was already in place. The customer started with the outputs and worked backward to design a minimal database with minimal input requirements.
从输出结果逆向工程到数据库再到输入来建构系统的方法被证明是一种非常有效和有效率的系统开发方法。几乎在关系数据库开始流行的同时,这种方法也发展了起来。使我们能够建立起运作良好,规范化的数据库。除此之外,这种思想也适用于创建最小系统(minimal systems).事实上,我们的一个客户在重建一个系统时已经使用了这种方法并取得了成功。该客户从输出入手,通过逆向工程设计了一个满足最小输入需求的最小数据库。
The new system had only about one-third the data elements of the system it was replacing. This was a major breakthrough. These developers came to understand that creating minimal systems had enormous advantages: they were much smaller and therefore much faster to implement, and they were also easier to understand and change, since everything had a purpose.
新系统只有老系统三分之一的数据元(data elements )。这是一个大的突破。开发人员开始逐渐认识到建立最小系统有着巨大的优势:系统更小因而可以更快的实现;功能单一更能适应变化。
Still, building minimal systems goes against the grain of many analysts and programmers, who pride themselves on thinking ahead and anticipating future needs, no matter how remote. I think this attitude stems from the difficulty that programmers have had with maintenance. Maintaining large systems has been so difficult and fraught with problems that many analysts and programmers would rather spend enormous effort at the front end of the systems development cycle, so they don't have to maintain the system ever again. But as history shows, this approach of guessing about the future never works out. No matter how clever we are in thinking ahead, some new, unanticipated requirement comes up to bite us. (How many people included Internet-based e-business as one of their top requirements in systems they were building 10 years ago?)
然而,创建最小系统并不符合许多分析员和程序员们的想法,不管有多遥远,他们总认为自己可以超前思考并预见到未来的需求。我认为这源于软件难于维护的原因。维护一个大的系统是如此的困难并充斥着问题,以致于许多分析员和程序员宁愿在系统开发的前期花费大量的精力来设计一个"完善"的系统,以求一劳永逸。然而事实证明,预测未来是徒劳的。不论我们有多聪明,思想有多超前,总会有一些不曾预料到的需求出现。(有多少人能够在10年前就将基于internet的电子商务作为未来的需求写入自己的软件)
Ultimately, one of the reasons that maintenance is so difficult revolves around the problem of changing the database design. In most developers' eyes, once you design a database and start to program against it, it is almost impossible to change that database design. In a way, the database design is something like the foundation of the system: once you have poured concrete for the foundation, there is almost no way you can go back and change it. As it turns out, major changes to databases in large systems happen very infrequently, only when they are unavoidable. People simply do not think about redesigning a database as a normal part of systems maintenance, and, as a consequence, major changes are often unbelievably difficult.
最后,维护如此困难的原因之一在于,当改变数据库设计时,其他的问题都会接踵而来。在大多数开发人员看来,一旦设计好数据库并在此基础上开始了编码以后,再去改变数据库的设计几乎是不可能的。在某种程度上,设计数据库就好比建造系统的地基:一旦你把混凝土灌了进去,你就没办法再去改变它。因此,除非不可避免,大型系统中的数据库极少会发生大的改动。人们不能把重新设计数据库仅仅当成系统维护的普通一部分。否则的话,对系统进行大的改动会变的难以想象的困难。
文章来源于领测软件测试网 https://www.ltesting.net/