## Hints

Identifying problematic designs in a software system via measurements is a delicate activity which requires expertise in both data collection and interpretation. Below are some hints you might consider to get the best out of the raw numbers.

* Which tool to use? There are many tools — commercial as well as public domain — which measure various attributes of source code entities. Nevertheless, few development teams make regular use of such tools and therefore it is likely that you will have to look for a metrics tool before applying this pattern.

In principle, start by looking at the tools used by the development team and see whether they can be used to collect data about the code. For instance, a code verification tool such as lint can serve as basis for your measurements. Start looking for a metrics tool only when none of the development tools currently in use may collect data for you. If that's the case, simplicity should be your main tool adoption criterion as you do not want to spend your precious time on installing and learning. The second tool adoption criterion is how easy the metrics tool integrates with the other development tools in use.

* Which metrics to collect? In general, it is better to stick to the simple metrics, as the more complex ones involve more computation, yet will rarely perform better.

For instance, to identify large methods it is sufficient to count the lines of code by counting all carriage returns or new-lines. Most other method size metrics require some form of parsing and this effort is usually not worth the gain.

* Which metric variants to use? Usually, it does not make a lot of difference which metric variant is chosen, as long as the choice is clearly stated and applied consistently. Here as well, it is preferable to choose the most simple variant, unless you have a good reason to do otherwise.

For instance, while counting the lines of code, you should decide whether to include or exclude comment lines, or whether you count the lines after the source code has been normalized via pretty printing. However, when looking for potential design problems it usually does not pay off to do the extra effort of excluding comment lines or normalizing the source code.

* Which thresholds to apply? Due to the need for reliability, it is better not to
to apply thresholds.<fnref n="2" /> First of all, because selecting threshold values must be done based on the coding standards applied in the development team and these you do not necessarily have access to. Second, thresholds will distort your perspective on the anomalies inside the system as you will not know how many normal entities there are.

* *How to interpret the results?* An anomaly is not necessarily problematic, so care must be taken when interpreting the measurement data. To assess whether an entity is indeed problematic, it is a good idea to simultaneously inspect different measurements for the same entity. For instance, do not limit yourself to the study of large classes, but combine the size of the class with the number of subclasses and the number of superclasses, because this says something about where the class is located in the class hierarchy.

However, formulas that combine different measurements in a single number should be avoided as you lose the sense for the constituting elements. Therefore it is better to present the results in a table, where the first column shows the name of the entity, and the remaining columns show the different measurement data. Sorting these tables according to the different measurement columns will help you to identify exceptional values.

* *How to identify anomalies quickly?* Although it is possible to identify exceptional values in a tabular representation of measurement data, such an approach is tedious and error-prone. Most metric tools include some visualization features (histograms, scatter plots, ...) to help you scan large volumes of measurements and this is usually a better way to quickly focus on potential design problems.

* *Should I browse the code afterwards?* Measurements alone cannot determine whether an entity is truly problematic: some human assessment is always necessary. Metrics are a great aid in quickly identifying entities that are potential problems but code browsing is necessary for confirmation. Note that large entities are usually quite complicated, thus understanding the corresponding source code may prove to be difficult.

* *What about normal entities?* Experienced programmers tend to distribute important functionality over a number of well-designed components. Conversely, exceptional entities are quite often irrelevant as truly important code would have been refactored. Therefore, you should be aware that you are only applying a heuristic: its possible that you are studying code which does not represent a design problem simply because it is deemed unimportant.

<footnote n="2">²Most metric tools allow you to focus on special entities by specifying some threshold interval and then only displaying those entities where the measurements fall into that interval.</footnote>
# Tradeoffs

## Pros

* *Scales well.* Metrics are readily applicable to large scale systems, mainly because with metric tools about 20% of all the entities require further investigation. When different metrics are combined properly (preferably using some form of visualization) one can deduce quite rapidly which parts of the system represent potential design problems.

* *Overview mode is appealing.* With proper tool support you can produce visual representations of the metrics data that provide immediate insight into the good as well as the problematic parts of the design.

## Cons

* *Results are inaccurate.* Some of the entities having exceptional measurements will turn out not to be problematic. Metrics are only a heuristic and false positives are likely to occur. Moreover, the metric may reveal problems that are not worth solving because the solutions will not contribute to your reengineering goal. Unfortunately, this you will only know after you analyzed the source code.

* *Missing priorities.* Identifying a potential problem is easy, the real difficult part is assessing the severity of the problem. Especially during a reengineering project, you identify far more problems than you have time to solve. Prioritizing the list requires a good understanding of both the system and the reengineering project.

## Difficulties

* *Data is tedious to interpret.* To measure the quality of a piece of code, you must collect several measurements. Interpreting and comparing such multi-valued tuples is quite tedious especially when dealing with large software systems. Therefore, use visualizations which allow you to analyze different measurements simultaneously.

* *Requires expertise.* The interpretation of measurement data is difficult and requires a lot of expertise. Fortunately, part of this expertise is documented in the form of design heuristics (see among others [Rie96] [LK94]) and the rest can be acquired on the job.
<figure><img src="image_18.png" /><figcaption>Figure 4.6: Class size overview with node size showing the lines of code and gray value showing the number of instance variables.</figcaption></figure>

## Example

The analysis of the database and the design of *XDoctor* was quite reassuring. Although there were some things to improve, the overall quality was quite good. Yet, you want to confirm this feeling and therefore plan to collect a number of quality metrics and visualize them. (Of course the visualization can be done with ordinary spreadsheets, but in this case you decide to use the CodeCrawler tool [DDL99] [Lan99].)

*Class Size Overview.* As a starter, you get an impression of the raw physical size of all the classes constituting *XDoctor*. You measure the class size in terms of number of lines of code (LOC) and number of instance variables (NIV) and use a *checkers graph* to show the relative proportion of the sizes. In such a graph all nodes are shown as squares where the size of the square is proportional to one size (here LOC) and the gray value is proportional to another size (here NIV).

Figure 4.6 shows the checker graph for *XDoctor*. The picture reveals that the class size is distributed quite evenly — which is reassuring — with a few noteworthy exceptions. For instance, there is the class B (with 1495 it is the largest in terms of lines of code) and class L (has most instance variables and second most lines of code). The classes in row Z are exceptional in the sense that they are very small, some of them even empty.

*Class Inheritance.* Next, you get a feeling for the way inheritance is used by studying the various subtrees in the inheritance hierarchy. Therefore, you measure the classes in terms of hierarchy nesting level (HNL) and number of descendant classes (NDC). You include size measurements as well to assess the magnitude of the classes within the inheritance tree. Therefore, you collect the number of methods (NOM), number of instance variables (NIV) and number of lines of code (LOC) as well. You use an *inheritance*
<figure><img src="image_19.png" /><figcaption>Figure 4.7: Inheritance tree focussing on class size. The node width shows the number of instance variables, the node height shows the number of methods and the gray value shows the number of code lines.</figcaption></figure>

tree to visualize the various subtrees and the proportion of class sizes inside each of them. All nodes in such a tree have a rectangular shape where the height, width and gray value of each node show three measurements.

Figure 4.7 shows such an inheritance tree for *XDoctor*, where the height, width and gray value of each node represent NOM, NIV and LOC. To the left, you observe several normal inheritance trees, namely small ones where the size of the classes is quite similar. One exceptional value is the same B you noticed earlier, however you now see that it also has a large superclass A (defining 70 methods), making it even more suspicious. The L you've seen before appears here as a solitary class. The hierarchies rooted in K, F and G seem quite interesting: they go deep (4 levels of inheritance) and have one large root class plus many smaller subclasses. H and I, plus M and N are both cases of large sibling classes, which may imply that too little is inherited from the common superclass. This must be verified via code browsing however.

*Method Inheritance.* To analyze particular inheritance trees in further detail, you investigate how methods in a subclass relate to methods in their superclass. Therefore, you produce a table showing for each class the number of methods overriding a method defined in a superclass (NMO), the number of methods added to the superclass (NMA) and the number of methods extending a method defined in a superclass (NME). Here as well you use an inheritance tree to identify exceptional values in the measurements.

Figure 4.8 shows the A, G and F subtrees identified earlier, but now the height, width and gray value of each node represent NMO, NMA and NME. The root classes are displayed as narrow white rectangles, which is
<figure><img src="image_20.png" /><figcaption>Figure 4.8: Inheritance tree focussing on method inheritance. The node width shows the number of methods added, the node height shows the number of methods overridden and the gray value shows the number of methods extended.</figcaption></figure>

normal as root classes cannot override nor extend. As far as the subclasses concerns, you observe two phenomena. On the one hand, the subclasses of A add a lot, yet override very little, which suggests that code reuse is the main purpose of this inheritance tree. On the other hand, the subclasses of F and G override more methods than they add, which suggests a lot of hook methods and an inheritance tree aimed at specializing behavior. Here as well, these assumptions must be verified by code browsing.

*Method Size Overview.* An example of how to identify potential problems in the method bodies concerns the ratio of lines of code (LOC) and the number of messages sent (MSG). In most method bodies, these two measurements will correlate but methods where this correlation does not hold typically represent special code.

To study this correlation relationship one might divide the two measurements.<fnref n="3" /> However, then you lose the sense for the constituting measurements which makes interpretation difficult. Therefore, you visualize the relationship by means of a *correlation graph*, where each method is shown as a small square and where the x, y position shows the measurements that are supposed to correlate. In such a graph, the nodes where the measurements correlate cluster around a diagonal, while the exceptions are from the diagonal.

Figure 4.9 shows a correlation graph where the horizontal axis (left to right) represents the number of messages sent and the vertical axis (top to bottom) the number of lines of code. You observe a big cluster in the top left corner where most nodes are superimposed on each other. This is

<footnote n="3">³Metrics theory prohibits arbitrary manipulations of numbers; one should first verify whether the scale of the measurement permits the calculation [FP96]. However, both are counting measurements having a ratio scale and then division is permitted.</footnote>
<figure><img src="image_21.png" /><figcaption>Figure 4.9: Correlation graph, with x-position showing the number of messages sent and y-position showing the lines of code.</figcaption></figure>

reassuring because it implies that most methods have fewer than 15 lines of code and 10 messages sent. The exceptions appear at the edges of the picture. For instance, node A is a large method with 99 messages packed on 45 lines of code. Node D (and its neighbors) are also methods where many messages are packed on a single line of code. Via code browsing you see that many of them are initialization methods. At the other side of the diagonal there is node B, which represents a method with 16 lines of code yet no messages sent. Code browsing reveals that it's a case where the whole method body has been commented out.

## Rationale

*You cannot control what you cannot measure.*

— Tom De Marco, [Mar82]

In several places in the literature it is mentioned that measuring source code helps in problem identification (see among others, [LK94] [FP96] [MLM96] [Nes88]). Most metric tools applied during these experiments visualize information by means of histograms and Kiviat diagrams. However, few research have studied the impact of thresholds while identifying exceptional entities; our own experience is that thresholds don't really
matter [DD99].

Unfortunately, the current research is inconclusive with regards to the accuracy of the results. Up until now, no experiments exist that count how many problems remain undiscovered, nor is there any work on assessing the severity of the problems discovered. As such it is impossible to assess the reliability of metrics for reverse engineering.

## Known Uses

During the FAMOOS project one event provided anecdotal evidence for how well a simple approach may outperform more specialized and complex approaches. Once we visited a business unit for a few days to demonstrate our CodeCrawler tool. At first the developers were quite sceptical because they felt like they would see "yet another metrics tool". The first surprise came when we showed them results already during the first day. They told us that other tools would typically require several days configuration time before they could parse their C++ code because it made such heavy use of special C++ features and macros. Moreover, and this was the second surprise, this simplicity did not diminish the quality of our results. The programmers confirmed most of the design anomalies we discovered, yet were intrigued by some observations we made. During the subsequent discussions they at least considered design alternatives.

## What Next

Applying this pattern will result in an overall impression of design quality and the identification of a few potential design problems. With this knowledge you should at least reconsider whether the goal of your reengineering project is still attainable. If it is, you will probably want to solve some of these design problems, for instance using patterns in Redistribute Responsibilities [p. 239] and Transform Conditionals to Polymorphism [p. 269]. Solving some of these problems may require a more detailed understanding of that design, which may be obtained by patterns in Detailed Model Capture [p. 117].
<figure><img src="image_22.png" /></figure>

# Detailed Model Capture

The patterns in First Contact should have helped you to get acquainted with the software system, while those in Initial Understanding should have helped you to understand which are the most important entities in the system. Your main priority now is to build up a detailed model of those parts of the system that will be important for your reengineering effort.

Most of the patterns concerned with Detailed Model Capture entail considerably more technical knowledge, use of tools and investment of effort than the patterns we have applied up to now. This is only natural, since only after you have built up your Initial Understanding can you determine whether more intensive investment of effort will pay off.

## Forces

Although you already have an impression of the system, there are several forces at play that may make it difficult to extract a more detailed model:

* *Details matter.* As argued by Brooks [Bro87], software engineering is different from other engineering disciplines because of the inherent lack of abstraction barriers. Other engineering disciplines rely on the laws of nature to hide irrelevant details, but software engineering must build on less solid foundations. *Consequently, it is essential to pay attention to the details.* The only question is how to filter out those details that do not matter, because you cannot possible investigate everything.

* *Design remains implicit.* As you read the code, many design decisions
sions will become apparent to you, but it will not be clear why and
how these decisions were made. In particular, it will be hard to tell
which design decisions were easy to make, and which of them cre-
ated a lot of grief. Nevertheless, such knowledge is crucial during
a reengineering project because you want to avoid making the same
mistakes over and over again. Consequently, once you discover the un-
derlying design rationale, make sure that it is properly recorded. This way,
your successors will be able to build on your discoveries rather than
be forced to reinvent the wheel.

* *Design does evolve.* Change is an essential ingredient of a successful system, certainly in object-oriented development processes with their emphasis on iterative development. As a consequence, design documents will always be out-of date with respect to the actual situation. However, this also implies that change itself is the key to understand how and why the design of a system has evolved the way it is. Consequently, *assume that important design issues will be reflected in the source code and in the way this code has changed over time.*

* *Static structure versus Dynamic behavior.* Object-oriented source code tells you which classes are defined, and how they are arranged in a class hierarchy. It is much harder to see which objects are instantiated at run-time, and how they collaborate to support the system. On a fine-grained level however, the latter is much more relevant than the former, especially due to the use of polymorphism. Consequently, *to extract the detailed design one must inevitably study the dynamic behavior.*

Overview

The patterns of Detailed Model Capture propose a series of activities that help you to expose design artifacts that are hidden in the code. Although some of these patterns, in particular Tie Code and Questions, are lightweight, most of them entail considerable effort, so you should evaluate carefully how much you expect to get out of applying them.

Figure 5.1 suggests some possible relationships between the patterns. Tie Code and Questions is perhaps the most fundamental of these patterns, and the easiest to apply. As you work through the source code, keep track of comments, questions, hypotheses and possible actions to perform by directly annotating the source code *at the point where the comment applies*. This pattern works well with the other patterns in this cluster, and can be productively applied throughout a reengineering project.

Refactor to Understand helps you to expose the design of cryptic code.
<figure><img src="image_23.png" /><figcaption>Figure 5.1: The patterns of Detailed Model Capture help you to expose the design of the software system and keep track of your understanding.</figcaption></figure>
It is important to understand that the intent of this pattern is *not* to im-
prove the code base itself, but only to improve your understanding. It
might well be that you decide to keep the results of your refactorings, but
this should not be your goal at this point. Your refactorings should instead
be treated as experiments to test various hypotheses concerning the code.

Since the source code gives you only a very static view of the class
hierarchy, it is useful to Step Through the Execution to learn what objects
are instantiated and run time and how they interact.

Although it is very easy to extract the interfaces of the classes in the
system, this will not tell you very much about how these interfaces can or
should be used. What you really need is to do is Look for the Contracts
supported by each class. The contracts tell you which client-supplier rela-
tionships exist, and how the public interface of a class supports that rela-
tionship. Idiomatic coding practices and design patterns typically express
such contracts in direct way, so you should train yourself to recognize
them.

Finally, though you may be able to extract various design artifacts from
the source code, you will not necessarily be able to get an insight into how
the system evolved that way. In particular, you may wonder whether cer-
tain design decisions were really justified, or whether they were arbitrary,
and you may wonder how stable parts of the design are. By comparing
different versions of the code base and focussing on places where func-
tionality was *removed* or refactored, you will be able to Learn from the
Past.

What Next

Now that you have mastered the details of a part of your system, it is a
good time to prepare for the actual reengineering by applying the patterns
in Tests: Your Life Insurance!. In particular, as you Refactor to Under-
stand, it is a good idea to Write Tests to Understand [p. 179], as this will
give you confidence in your experiments. Also, patterns like Step Through
the Execution, Look for the Contracts and Learn from the Past help you
to see which components implement what functionality: this knowledge
must be used to Test the Interface, Not the Implementation [p. 171] and to
Record Business Rules as Tests [p. 175].
## 5.1 Tie Code and Questions

**Intent** Keep the questions and answers concerning your reengineering activities synchronized with the code by storing them directly in the source files.

### Problem

How do you keep track of your understanding about a piece of code and the questions that you have, keep these remarks synchronized with the code during its future evolution, and share them with the other members of your team?

*This problem is difficult because:*

* Writing up what you know and don't know about the system you are analyzing is tedious and time-consuming.

* Your understanding is a moving target, so it is hard to keep a written document up-to-date.

* If you don't write down your questions and insights as soon as they occur to you, you will not be able to keep track of them.

* You want to share your knowledge with the team to maximize its value.

* Logging questions and answers in log files, bulletin boards or email distribution lists may be convenient for disseminating knowledge within the team, and may provide a convenient searchable history of the team's understanding, but when you are looking at a piece of code, it will be hard to tell what questions and answers pertain to it.

*Yet, solving this problem is feasible because:*

* You can annotate the code, and therefore record your understanding physically close to the code element it refers to.

### Solution

While you are working on the code annotate it directly and immediately with the questions you are facing.

In principle there are two ways to annotate the code.
* *Comment-based Annotations.* This approach uses the commenting conventions of the programming language and as such is better-suited for a text-oriented environment. A few conventions are needed to distinguish the normal comments from the annotations.

/* #to: John #by: SD #on: 3/12/99 *****
Screws up when we have nested IFs. */

Basic tools part of your program environment can then be used to search and modify annotations. With a little bit of extra effort one can easily build tools to query, extract and cross-index all comment-based annotations.

* *Method-based annotations.* This approach exploits the possibility to query which method invokes a given method, a feature provided by many of today's programming environments. The idea is to declare a global method accepting a few strings as an argument and having an empty method body. Each time you want to annotate a particular piece of code, you invoke that method passing your annotations as a parameter.

this annotateCode("#to: John #by: SD #on: 3/12/99",
"Screws up when we have nested IFs.");

You can then use the querying and browsing facilities of your pro-
gramming environment to identify the locations where this special
method is invoked, thus where the annotations occur. Most pro-
gramming environments can be extended by means of little scripts,
in which case it is possible to develop tools to generate reports about
all annotations.

Note that the less you change the code, the less likely it is that you
will introduce errors. This makes the comment-based version safer
than the method-based version.

hints

* Record your annotations *as close as possible* to the code to which they refer.

* Annotations may be *questions, hypotheses, "to do" lists, or simply observations* about the code that you wish to record for future reference.

* Use conventions to *identify your annotations*. In a team context, include, for example, the initials of the developer that made the comments and the date the comment was entered. This way you can easily query them.
* *Follow the corporate practices.* If comments are written in a language other than English, continue if you can. However, if you have the choice never write your annotations in a language different from that in which the source code is written (English in most cases). Otherwise, you create a different context and force the reader to switch between them.

* When you discover the *answer* to any one of your questions, *immediately update* the annotation for the benefit of future readers, or simply *delete* the question if it is no longer relevant.

# Tradeoffs

## Pros

* *Natural Synchronization.* You keep the code and the annotations in close physical proximity, and you thereby improve your chances of keeping them in sync. While modifying the code, you will more naturally modify the annotations, or remove them if they become obsolete.

* *Improves Team Communication.* Tie Code and Questions avoids that team members must open an extra communication channel (e-mail, bulletin boards, ...). They must read the code they work with any- how so you can multiplex the code as a communication channel.

* *Minimize Context Description.* When you annotate the code you are immediately in context. This way you will minimize the need to describe the context of your questions and keep your effort low while documenting your questions and annotations.

## Cons

* *Passive in Nature.* Questions that you enter are not necessarily directed to anyone and even if they are, it is not certain that the addressee will read them or answer them in time. Additional tools are needed to collect the annotations and maybe even notify the appropriate persons.

* *Process Incompatibility.* Many companies are organized around a hierarchical reporting structure. Tie Code and Questions may be rejected by these organizations because it circumvents the normal communication channels. Also, some corporate practices impose strong constraints on what programmers are allowed to do with the code,
which may limit the potential if this pattern. For instance, if annotations cannot be removed when they become obsolete, they will create too much noise to be useful.

## Difficulties

* *Finding the Right Granularity.* As with any kind of comments, you should take care to introduce just the right amount of detail. Terse or cryptic annotations quickly lose their value, and verbose annotations will distract the reader from the code itself.

* *Motivating the Programmers to Write Comments.* Programmers generally do not like to write comments or documentation. One way of motivating them is to use the annotations during code reviews or status meetings: this way the comments have an immediate benefit.

* *Quality of the Answers.* As with any other kinds of documentation, it may happen that wrong answers are given. One way to deal with this situation is to review the annotations regularly within the team.

* *Eliminating the Annotations.* On certain occasions you may wish the remove the annotations. For instance, if you must deliver a "clean" version of the source-code to your customer, or if your compiler isn't smart enough to remove an invocation of an empty method body. In that case, make sure that you have the proper tools to filter out the annotations.

## Rationale

This pattern has its roots in *literate programming* [RS89][Knu92]. A literate program reverses the usual relationship between program text and comments: executable code is embedded within documentation, not the other way around. Literate programming puts the emphasis on keeping the code and its documentation physically close. The physical proximity reduces the effort spent in keeping the code and its documentation in sync.

## Known Uses

*Comment-based annotations.* Various programming environments provide implicit support for managing annotations within the code. Emacs, for example, has a built-in tool, called e-tags, which allows you to easily generate a cross-reference database of a set of files [CRR96]. The Eiffel environment, on the other hand, allows you to assign different levels of visibility to your comments (and your code). If you assign private scope to
your annotations you can easily separate the annotations yet make sure
that these will not be seen externally.

The company MediaGeniX — a Belgian company operating in the multi-media sector — used a systematic code tagging mechanism to record information about changes. The programming environment was altered in such a way that every change to the code was automatically annotated with a tag that describes the motivation for the code change (bug fix, change request, new release), the name of developer, and the time of the modification. Only the last tag is kept in the code, but via the configuration management system it is possible to inspect previous tags and changes. The tag also includes a free field where the developers may write what they want and is often used for questions and answers.

*Method-based annotations.* The Squeak development team [IKM+97] used this technique not so much to keep track of questions but as a means to facilitate communication in an open-source development project. In this team comments were introduced by invoking the method flag: defined in the class *Object*. Developers can query all senders of the flag: message to locate annotations. Furthermore, the method is defined to accept a symbol as its argument. This makes it possible to search more specifically, for example, for all the annotations flagged with the symbol #noteForJohn.

Object>>flag: aSymbol

"Send this message, with a relevant symbol as argument, to flag a message for subsequent retrieval. For example, you might put the following line in a number of messages:

self flag: #returnHereUrgently

Then, to retrieve all such messages, browse all senders of
#returnHereUrgently."

Figure 5.2 shows on the top pane all the senders of the flag: message in the Squeak2.7 environment. The bottom pane then shows the code of the method `removeEmptyRows` that contains a call to the method flag: highlighted. The flag: message is sent with argument #noteToJohn. The actual content of the annotation follows as a comment.

## Related Patterns

Tie Code and Questions works well in tandem with Refactor to Understand. Questions in the code may often be resolved by refactoring it. Conversely, as you Refactor to Understand, new questions will be raised and can be entered as annotations.
<figure><img src="image_24.png" /><figcaption>Figure 5.2: Finding all senders of a message in Squeak.</figcaption></figure>
## 5.2 Refactor to Understand

**Intent** Iteratively refactor a part of a software system in order to validate and reflect your understanding of how it works.

### Problem

How can you understand a cryptic piece of code?

*This problem is difficult because:*

* Cryptic code is difficult to read, hence to understand.

* You may have some idea how the code works, but it is hard to verify because the code does not reflect your ideas.

*Yet, solving this problem is feasible because:*

* The piece of code is relatively small and has clearly defined boundaries.

* Your development tools allow for rapid edit-compile cycles, so you can make some small changes and check whether you're still able to compile the source-code or that your tests still run.

* You have a source-code browser that allows you to query dependencies between source-code entities (i.e., which methods invoke a given operation, which methods access a given attribute, ...), so that you can infer its purpose.

### Solution

Iteratively rename and refactor the code to introduce meaningful names and to make sure the structure of the code reflects what the system is actually doing. Run regression tests after each change if they are available, else compile often to check whether your changes make sense. Decide what to do with the code after you have refactored it.

### Hints

Your primary goal here is to *understand the system*, not to improve the code. The changes you make to the code should therefore be treated as “experiments” to test your understanding of the code. As a consequence, you
should make a copy of the code before you start. After you have refactored the code, it is possible that you release any of the changes you make, but you do not want to make that decision up front. Perhaps your refactoring experiments will actually improve the code, but it is just as likely that you will make a mess of things since you do not yet understand the code. It does not really matter at this stage. After a first experience you will be in a better position to do a proper job of refactoring.

It is hard to do a good job of refactoring without having tests in place to verify that your changes have not broken anything. If adequate tests do not exist, you should *not* seriously consider keeping the results of your refactoring experiments. However, consider applying Write Tests to Understand [p. 179] in tandem with Refactor to Understand.

You should select refactoring operations that will make design decisions more explicit in the code. The typical refactorings applied during this iterative restructuring are Rename Attribute [p. 318], Rename Method [p. 318], and Extract Method [p. 317].

The following guidelines will help you to find out where and how to apply these refactorings to improve the readability of the code. Many of these guidelines are considered to be just good, standard practice in Smalltalk programming [Bec97]. They apply, however, equally well to other programming languages. They can be applied in any order; each of them participates in the understanding of the others.

* Rename attributes to convey roles. Focus on attributes with cryptic names. To find out about their roles, look at all the attribute accesses (including invocations of accessors methods). Afterwards, rename the attribute and its accessors according to its role, update all references and re-compile the system.

* Rename methods to convey intent. To retrieve the intent of a method that does not have an intention revealing name, investigate all invocations and attribute uses, and deduce the method's responsibility. Afterwards, rename the method according to its intent, update all invocations and re-compile the system.

* Rename classes to convey purpose. To capture the purpose of class having an unclear name, investigate clients of the class by examining who is invoking its operations or who is creating instances of it. Afterwards, rename the class according to its purpose, update all references and re-compile the system.

* Remove duplicated code. If you identify duplicated code, try to refactor it into a single location. As such, you will identify slight differences that you probably would not have noticed before refactoring and that are likely to reveal some subtle design issues.
* *Replace condition branches by methods.* If you encounter conditions with large branches, extract the leaves as new (private) methods. To name these methods, study the condition until you understand it well enough to choose an intention revealing name.

* *Refactor method bodies to a consistent level of abstraction.* Long method bodies with comments separating blocks of code violate the rule of the thumb that all statements in a single method body should have the same level of abstraction. Refactor such code by introducing a new (private) method for each separated block of code; name the method after the intent recorded in the comment.

# Tradeoffs

## Pros

* *Expose design.* Not only will the refactoring process improve your understanding of the code, but this understanding will also become explicit in the structure of the code. This will make it easier to further document that understanding by means of Test Code and Questions or Write Tests to Understand [p. 179].

* *Incremental validation.* Normally, understanding does not arise as part of a single revelation, but as the result of an iterative process in which earlier understanding is the base for the next iteration. Refactor to Understand encourages such an approach, because of its emphasis on small steps and frequent verification (either by running tests or either by compiling often).

## Cons

* *Risk of introducing errors.* The less you change the code, the smaller your chances of introducing errors. Small refactorings should be behavior-preserving, but it may be non-trivial to verify that even simple refactorings do not break the code. If you do not have adequate regression tests in place, it can be risky to introduce changes, or costly to develop the needed tests. For these reasons it is important to attempt to Refactor to Understand only on a working copy of the software.

## Difficulties

* *Tool Support.* Manually refactoring code can be tedious and risky [FBB+99]. Various tools, like the Refactoring Browser [RBJ97],
greatly simplify the task of refactoring, and especially help to apply
non-trivial refactorings such as Extract Method.

* *Acceptance of Changes.* Refactoring someone else's code may prove a lot harder than refactoring your own. A lot of companies have a strong culture of code ownership, so improving someone else's code is often considered an insult. That is one of the reasons why you should not necessarily release the refactored version to the rest of the team.

* *When to stop.* It is often difficult to stop changing code when you identify problems. Remember that your primary goal here is to just understand the system. When you have achieved that goal, it is time to stop.

## Known Uses

Don Roberts and John Brant coined the term Refactor to Understand at ESUG '97 and Smalltalk Solutions '97 during a demonstration of the Refactoring Browser. They showed how they gradually understood an algorithm by renaming and refactoring its code. During the subsequent iterations of the pattern, the code slowly started to make sense and the design gradually became explicit in the code.

We applied this pattern ourselves during a FAMOOS case study. We had to understand a single method of about 3000 lines of C++, which was a deeply nested conditional. We first replaced the leaf condition branches by methods, gradually working our way up the nesting structure. After several iterations, we discovered that this method was actually implementing a complete parser for a small command language.

Harry Sneed reports several reengineering projects where a large Cobol program was refactored by removing all goto statements. However, he was later forced to reintroduce the go-to statements because the developers rejected his changes [Sne99].

## Related Patterns

"Arranging the Furniture" [Tay00] is a pattern to help newcomers feel at home when they start in a new project. The pattern solution is: "An adopter should be encouraged to 'move in' by cosmetically arranging the code."
## What Next

Refactor to Understand works well in tandem with Tie Code and Questions. Refactorings are more expensive to implement than simply annotating the code, so first annotate, and then refactor. Also, consider to Write Tests to Understand [p. 179] as you refactor. These two activities reinforce each other since tests document your understanding of how a software artifact works, and refactoring helps you to expose its design. Furthermore, tests will help you to verify that your refactorings didn't break anything.

When you have finished a round of Refactor to Understand, you must decide what to do with your changes. If you discard the experimental code, you should consider applying Tie Code and Questions to annotate the code base with the knowledge you have acquired.
## 5.3 Step Through the Execution

**Intent** Understand how objects in the system collaborate by stepping through examples in a debugger.

### Problem

How do you discover which objects are instantiated at run-time and how they collaborate?

*This problem is difficult because:*

* The source code exposes the class hierarchy, not the objects instantiated at run time and how they interact.

* Collaborations are typically spread out through the code. Although it is easy to see which classes and methods are defined in a system, it can be hard to tell by reading the source code alone which sequence of events will lead to an object being created or a method being invoked.

* In the presence of polymorphism, it can be especially difficult to tell which objects are clients of which service providers. Just because an object uses a certain interface that another object provides, does not mean that the former is actually a client of the latter.

* Reading the code will not tell you what concrete scenarios can take place. The actual flow of execution will depend on the internal state of all participating objects and this cannot be inferred directly from the source code.

* The source code will not tell you which objects are long-lived and which are ephemeral (i.e., local to the execution of a single method).

Yet, solving this problem is feasible because:

* You are aware of some typical usage scenarios.

* You can run the code inside a debugger.

* Your attention is focussed on part of the system.

### Solution

Run each of the scenarios and use your debugger to step through the code. Observe which objects collaborate and how they are instantiated. After-
wards, generalize these observations and record your knowledge for future reference, possibly by means of Tie Code and Questions and Record Business Rules as Tests [p. 175].

## Hints

It is too time-consuming to step through every single statement of a running system. The assumption here is that you are focussed on some specific aspect of the system that is difficult to understand.

* Set *breakpoints* to interrupt execution when the system enters the code you are interested in.

* Change the *internal state* of the objects to see how alternative execution paths are triggered.

* Restart a method currently on the execution stack to quickly verify a similar scenario.

## Tradeoffs

### Pros

* *Realistic View.* By stepping through the running program, you get a precise picture of how the scenario unfolds. Moreover, you can inspect the internal state of the objects involved, see how new objects are created and observe which objects collaborate under which circumstances.

* *Handles complexity.* On a small scale it is possible to infer object collaborations from analyzing the source code. Slicing tools for instance may tell you which statements of the source code are affected by a given variable. For large and complex systems however, the number of possibilities and interactions is just too large. Therefore, the only reasonable way to learn how objects collaborate is to study the execution traces.

### Cons

* *Scenario-based.* Your must restrict yourself to a limited set of scenarios, hence the observed object-collaborations are necessarily incomplete. Of course you must do your best to choose representative scenarios. Unfortunately, this choice brings you back to square one, because the only way to be sure that you have a representative
set of scenarios is to verify whether they cover all possible object-collaborations.

* *Restricted Applicability.* For systems where time plays a crucial role, stepping through the execution will give you an unrealistic view of the system's behavior. Worse, for concurrent or distributed systems the mere fact of stepping through concurrent code may perturb the execution of the system itself. As such, you get the same effects as in Heisenberg's uncertainty experiments, where determining exact positions of quantum particles imply that other attributes about these particles become uncertain.

## Difficulties

* *Dependency on Tools.* You need to have good debugger to Step Through the Execution. Not only must it allow to set and remove breakpoints dynamically, it also should provide the means to examine the state of the objects involved. And to easily verify alternative paths, the debugger should allow you to change the internal state of an object, or even restart a method currently on the execution stack.

## What Next

You will need concrete scenarios in order to Step Through the Execution (possibly inferred from Interview During Demo [p. 69]). Consider encoding these scenarios as test cases. You can then iteratively Write Tests to Understand [p. 179] as you Step Through the Execution since the insights you gain into the states of collaborating objects can then be formulated as concrete tests.

As you Step Through the Execution, it is a good idea to keep an eye on the way collaborating objects use each other's interface. Afterwards, you can exploit the knowledge you have gained to Look for the Contracts.
## 5.4 Look for the Contracts

**Intent** Infer the proper use of a class interface by studying the way clients currently use it.

### Problem

How do you determine which contracts a class supports? That is, how do you know what a class expects from its client classes in order to function as intended.

*This problem is difficult because:*

* Client/supplier relationships and contracts are only implicit in the code. Although interfaces are easy to extract from the code, they do not necessarily tell you how to use them properly. If not explicitly documented, it can be hard to guess (a) the proper sequence in which methods should be invoked, (b) the valid parameters that should be supplied, (c) which methods should be invoked by which clients, (d) which methods should be overridden by subclasses.

* Typing and scoping rules often force programmers to compromise the provider's interface. Moreover, encapsulation constructs (e.g., public/private declarations) are frequently misused to cope with implementation issues. For instance, database and user-interface toolkits often require the presence of public accessor methods.

*Yet, solving this problem is feasible because:*

* You have a *good understanding* of the system's structure (for example obtained via Initial Understanding), so you can distinguish key classes from less important ones.

* You trust that the class is being used properly by its clients and its subclasses.

### Solution

Look for common programming idioms that expose the way clients make use of the class interface. Generalize your observations in the form of *contracts*, i.e., explicit declarations of what a class expects from its clients.
## Hints

Your goal here is to understand how classes collaborate by exposing the way in which the interface to a class is used by its different clients. Since an exhaustive analysis of the code will probably exhaust you, you need some way to expose the contracts without stepping through every single line of code.

Although contracts are only implicit in the code, most frequently there will be hints in the code that a particular relationship exists between various classes. These hints may manifest themselves as idioms particular to the programming language in use, conventions in use by the development team, or even common design patterns.

What precisely you should look for will depend on the context, but here are a few examples that are generally useful:

*Use Your Tools.* To get an overview of the relationships between classes, make the best use you can of the available tools. Although you could analyze the code by hand to infer relationships between classes, the process is tedious when applied to *more than a couple of classes*.

Many organizations use design extraction or round-trip engineering tools to document their systems. You can easily generate a draft view of the system you are analyzing without investing too much time. However, be prepared to be flooded with "boxes and arrows" diagrams containing irrelevant detail. Nevertheless, design extraction tools let you specify filters and ways to interpret code, so once your mappings are defined you can reuse them over multiple extractions.

The design overview can help you to identify key classes in the hierarchy (i.e., abstract classes that many other classes inherit from), part-whole relationships, and so on.

*Look for Key Methods.* Focus on the most important methods. With your knowledge of the system you will recognize key methods based on their signature.

* *Method Names.* Key methods are likely to bear intention revealing names [Bec97].

* *Parameter types.* Methods taking parameters with types corresponding to key classes in the system are likely to be important.

* *Recurring parameter types.* Parameters represent temporary associations between objects. When the same parameter types often recur in method signatures, they are likely to represent important associations.
*Look For Constructor Calls.* To understand how and when to instantiate objects of a particular class, look for methods in other classes invoking the constructors.

Pay particular attention to which parameters are passed to the constructor, and whether the parameters are shared or not. This will help you determine which instance variables are parts of the constructed object, and which are merely references to shared objects.

Invocations of constructor methods may reveal a *part-whole relationship*. When a client stores the result of a constructor method in an attribute then this client will probably serve as the whole. On the other hand, when a client passes itself as an argument to a constructor method it is likely to act as a part.

Invocations of a constructor method may also expose a Factory Method [p. 319] or even an Abstract Factory [p. 319]. If they do, then you know that you will be able extend the system by subclassing the class under study.

*Look for Template/Hook Methods.* To understand how to specialize a class, look for (protected) methods that are overridden by subclasses, and identify the public methods that call them. The public, calling method is almost certainly a Template Method [p. 322]. Check the class hierarchy to determine whether the overridden method is *abstract*, in which case subclasses must implement it, or whether a default implementation is provided. In the latter case, it is a *hook method*, and subclasses may choose to override it or be happy with the default.

For each template method check all other methods it invokes as these are likely to represent other hook methods.

*Look for Super Calls.* To understand what assumptions a class makes about its subclasses, look for super calls. Super calls may be used by subclasses to extend an inherited method in an *ad hoc* way. But very often super calls express the fact that a particular method *must not be overridden by subclasses* unless the overridden method is explicitly invoked by a super call.

This idiom is heavily used in Java by classes that define multiple constructors. Any subclass of `java.lang.Exception`, for example, is expected to define both a default constructor and a constructor that takes a `String` argument. Those constructors should do nothing in particular except invoke the super constructor so that the exception subclass will be correctly initialized.
# Tradeoffs

## Pros

* *Reliable*. You can trust the source code more than the documentation.

## Cons

* *Bad habits linger*. Just because certain practices appear in the code doesn't mean that's the right way to do things. The contracts that clients and subclasses adhere to are not necessarily the ones that the class actually supports.

* *Noise*. Browsing the source code is like mining — once in a while you will find a gem but you will have to dig through a lot of dirt first. By focussing your attention on idiomatic usages, you should be able to reduce the noise factor to a large degree.

# Known Uses

Many researchers have investigated ways to analyze how clients use a class interface. For instance, Brown [Bro96], Florijn [FMvW97] and Wuyts [Wuy98] have all shown that it is possible to find symptoms of design patterns in code. Also, Schauer et al. [SRMK99] report about a technique to semi-automatically detect hook methods based on analysis of overridden methods. The latter technique scales quite well, due to their particular way of visualizing class hierarchies and emphasizing classes where many methods are overridden, hence are likely to define hook methods. Additionally, Steyaert et al. [SLMD96] have shown that it is possible to capture how subclasses depend on their superclasses (they have named these dependencies *reuse contracts*) and afterwards detect potential conflicts when the superclasses gets changed.

# What Next

One way to validate the contracts you have identified is to Step Through the Execution. Conversely, as you Step Through the Execution you will uncover collaborations between various objects. At that point you may Look for the Contracts that govern those collaborations.

If the code is hard to read, you may wish to Refactor to Understand before you Look for the Contracts. To understand how the contracts evolved to their current state, you might Learn from the Past.
## 5.5 Learn from the Past

**Intent** Obtain insights into the design by comparing subsequent versions of the system.

### Problem

How can you discover why the system is designed the way it is? How can you learn which parts of the system are stable and which parts aren't?

*This problem is difficult because:*

* The lessons learned during a development process are rarely recorded in documentation. Furthermore, the developers' perceptions and memory of design decisions tend to warp over time. Therefore, you can only rely on source code and must reconstruct the learning process from there.

* The system is large and has been released in successive versions, and therefore you have a large quantity of source code to analyze. Text comparison tools (such as Unix `diff`) will not scale up for the sizes you're dealing with.

* Even if you have a tool to identify the changes between two subsequent releases, most of the changes will concern adding *new* functionality. For the reconstruction of the learning process and how this consolidated into the class design, you're main interest lies in what happened with the *old* functionality.

*Yet, solving this problem is feasible because:*

* You have a *good understanding* of the system's structure (for example obtained via Initial Understanding), so you're able to focus on appropriate subsystems.

* You have access to the *subsequent releases* of the system, so you can reconstruct the changes by comparing the source code of the versions.

* You have the means to examine what happened with individual source code entities. For instance, you have a *metrics tool* at your disposal, which allows you to quantify the size of entities in the source-code and use these numbers as a basis for comparison. As an alternative, you have a *configuration management* system that can provide you with information about particular changes to source-code entities.
* You have enough *expertise with refactorings* in the implementation language being used, so you are able to recognize refactorings from their effects on source-code. Moreover, once you know which refactorings have been applied, you can use this expertise to make an educated guess at the underlying design rationale.

* You have a *source-code browser* that allows you to query which methods invoke a given operation (even for polymorphic operations), so you can find out dependencies between classes and investigate how they are affected by the refactorings.

## Solution

Use the metrics or configuration management tool to find entities where functionality has been *removed*, because such entities are a sign of a consolidating design. Also, look for entities which change often as these may point you to an unstable part of the design.

## Hints

Your goal is to get a feeling for how and why the system has evolved to its current state. In particular, you want to understand which parts of the system have been heavily refactored, which parts have become stable, and which parts are hot spots of activity.

Portions of the software system that have been heavily extended are simply a sign of growth, not of evolution of the design. On the other hand, portions where software has been *removed* are signs that the design of the system has been altered. By understanding how it has been altered, you can obtain insights into the stability of the design.

**Unstable design.** If you detect repeated growth and refactoring in the same portion of the system, that should be a sign that the design is unstable. It may indicate opportunities to redesign that portion of the system to better accommodate the kinds of changes and extensions that habitually take place.

**Mature and stable design.** A mature subsystem will exhibit some growth and refactoring, followed by a period of stability. Early versions of the subsystem will show growth followed by refactoring, followed by a period in which only new classes and subclasses are added. As the hierarchy stabilizes, classes near the top of the hierarchy will exhibit only moderate growth, but little refactoring.
# Tradeoffs

## Pros

* *Concentrates on important design artifacts, because the changes point you to those places where the design is expanding or consolidating and this in turn provides insight into the underlying design rationale.*

* *Provides an unbiased view of the system, because you do not have to formulate assumptions about what to expect in the software (in contrast to top-down techniques like Speculate about Design [p. 97]).*

## Cons

* *Requires considerable experience, in the sense that the reverse engineer must be well aware of how the refactorings interact with the coding idioms in the particular implementation language.*

* *Considerable tool support is required, especially (a) a metrics tool or a configuration management system; (b) a code browsers that is able to trace back polymorphic method invocations.*

## Difficulties

* *Imprecise for many changes, because when too many changes have been applied on the same piece of code, it becomes difficult to reconstruct the change process.*

* *Sensitive to renaming, if one identifies classes and methods via their name<fnref n="1" />. Then rename operations will show up as removals and additions which makes interpreting the data more difficult.*

# Rationale

Many object-oriented systems came into being via a combination of iterative and incremental development (see [Boo94] [GR95] [JGJ97] [Ree96]). That is, the original development team recognized their lack of problem domain expertise and therefore invested in a learning process where each learning phase resulted in a new system release. It is worthwhile to reconstruct that learning process because it will help you to understand the rationale embodied in the system design.

<footnote n="1">¹Note that some configuration management systems keep track of renaming operations which will of course alleviate the problem.</footnote>
One way to reconstruct the learning process is to recover its primitive
steps. In object-oriented parlance, these steps are called refactorings and
consequently this pattern tells you how to recover refactorings like they
have been applied in the past. The technique itself compares two subse-
quent releases of the source code identifying entities that decrease in size,
because that's the typical symptom of functionality that has been moved
elsewhere.

Known Uses

We ran an experiment on three medium-sized systems implemented in Smalltalk. As reported in [DDN00], these case studies suggest that some simple heuristics can support the reverse engineering process by focusing attention on parts of the system where functionality has been removed. This way, we could for instance detect where a class had been split or where methods have been moved to a sibling class. Of course these refactorings must be examined in further detail to guess the intent behind the refactoring. This is never easy but in our experience has proven worthwhile. In one particular case for instance, we discovered several classes where methods had been moved to sibling classes. Closer examination revealed that the reengineer was moving these methods to break circular dependencies and was in fact introducing a layer.

Other researchers also report on examining changes to support the re-
verse engineering process. For instance, Ball *et al*. annotate code views
with colors showing code age [BE96]. On the other hand, Jazayeri *et al*.
use a three-dimensional visual representation for examining a system's
software release history [JGR99]. The same people have also investigated
which change requests affect which software modules to detect logical de-
pendencies between software modules [GHJ98].

What Next

Now that you discovered some stable parts in the design, you will proba-
bly want to reuse them. In that case take some precautions: first document
the interfaces of that part (see Look for the Contracts) and then write the
corresponding test cases (see Test the Interface, Not the Implementation
[p. 171]).

On the other hand, the unstable parts of the design should probably be
dismissed Nevertheless, if the unstable part seems crucial for your reengi-
neering project, then you must seek which change requests caused the in-
stability. In that case, Chat with the Maintainers [p. 45] or even Interview
During Demo [p. 69] and based on this knowledge decide how to restruc-
ture that part so that it is better suited for the kind of change requests that
come in.
# Part III

## Reengineering
<figure><img src="image_25.png" /></figure>
# Chapter 6

## Tests: Your Life Insurance!

You are at the beginning of a reengineering project. You know that you will have to perform radical surgery on many parts of a valuable legacy system. You are wondering how you will be able to minimize the risks of changing a system on which your business depends: the risk of *breaking features* that used to work, the risk of *spending too much effort on the wrong tasks*, the risk of *failing to integrate needed new functionality into the system*, and the risk of *further increasing maintenance costs*.

The patterns presented in this cluster present effective ways of using tests in a reengineering context to reduce the risks posed by reengineering changes.

*Caveat.* Testing is a rich and important subject that can scarcely be covered in any depth in the few pages we devote to it in this chapter. We have done no more than identify a few of the more significant testing patterns that are especially relevant to reengineering projects, and briefly sketch out some of the key issues. Binder, for example, devotes an entire book to testing object-oriented systems [Bin99].

### Forces

These patterns share common forces that concern various elements of risk for the evolution of the legacy system. Each pattern addresses some of these forces in order to achieve a certain balance between effort and risk.

#### Reengineering Forces

* Legacy systems often do not have test procedures defined.
* Changing parts of a system without introducing new bugs is a challenging task.

## System Development Forces

* Not every aspect of a system can be tested.

* Certain aspects are like concurrency and user interfaces are difficult to test.

* Under time pressure, writing tests is always the task that is eliminated first.

* Having all the knowledge of a system concentrated in only a few people poses a high risk for the future of the project.

## Human Forces (customers)

* Customers ultimately do not pay for tests but for new features in the system.

* An unstable or buggy system is not acceptable for customers.

## Human Forces (developers)

* Programmers believe they do not need tests, since they write good code.

* Programmers are not motivated by long term goals since they may leave the project in a month from now.

* Programmers are more interested in tools and processes that can reduce the time they are losing in identifying problems.

* Fixing bugs is not fun.

* Writing tests is not considered to be a noble task.

## Overview

As shown in Figure 6.1, Write Tests to Enable Evolution is the root of this cluster. It explains why systematic tests are critical to reengineering projects and what kinds of tests are necessary. It is based on Grow Your Test Base Incrementally which advocates strategies for introducing new tests as you need them.

In order to effectively manage incremental introduction of tests, it is important to Use a Testing Framework to structure and organize suites
<figure><img src="image_26.png" /><figcaption>Figure 6.1: When, why, how and what to test.</figcaption></figure>
of tests. The testing framework should support you in designing certain styles of tests. In particular, if you Test the Interface, Not the Implementation of components, by using black-box testing strategies, then your tests will tend to be more useful in the face of system changes. Furthermore, if you can Record Business Rules as Tests, then you will have an effective way to keep the business rules explicitly represented and continuously synchronized with the running system even in the presence of radical changes.

Tests may be introduced at various times for various reasons. Write Tests to Understand advocates investing testing effort in those parts of the system that you need to understand in order to implement changes. More specifically, it is a good idea to Test Fuzzy Features, to Test Old Bugs, and especially to Retest Persistent Problems.

The patterns in this cluster directly support Migration Strategies [p. 181] for reengineering: Regression Test After Every Change [p. 201] helps you build confidence by ensuring that everything still runs after every incremental change to the system. In effect, tests are a necessary precondition to Always Have a Running Version [p. 199], and they enable you to Migrate Systems Incrementally [p. 191].
## 6.1 Write Tests to Enable Evolution

**Intent** Protect your investment in the legacy code by imposing a systematic testing program.

### Problem

How do you minimize the risks of a reengineering project, specifically, the risks of:

* failing to simplify the legacy system,

* introducing yet more complexity to the system,

* breaking features that used to work,

* spending too much effort on the wrong tasks,

* failing to accommodate future change.

This problem is difficult because:

* Impact of changes cannot always be predicted because parts of the system may not be well-understood or may have hidden dependencies.

* Any change to a legacy system may destabilize it due to undocumented aspects or dependencies.

Yet, solving this problem is feasible because:

* You have a running system, so you can determine what works and what doesn't work.

* You know which parts of the system are stable, and which are subject to change.

### Solution

Introduce a testing process based on tests that are automated, repeatable and stored.
## Hints

Well-designed tests exhibit the following properties:

* **Automation.** Tests should run without human intervention. Only fully automated tests offer an efficient way to check after every change to the system whether it still works as it did before. By minimizing the effort needed to run tests, developers will hesitate less to use them.

* **Persistence.** Tests must be stored to be automatable. Each test documents its test data, the actions to perform, and the expected results. A test succeed if the expected result is obtained, otherwise it fails. Stored tests document the way the system is expected to work.

* **Repeatability.** Confidence in the system is increased if tests can be repeated after any change is implemented. Whenever new functionality is added, new tests can be added to the pool of existing tests, thereby increasing the confidence in the system.

* **Unit testing.** Tests should be associated to individual software components so that they identify clearly which part of the system they test [Dav95].

* **Independence.** Each test should minimize its dependencies on other tests. Dependent tests typically result in avalanche effects: when one test breaks, many others break as well. It is important that the number of failures represent quantitatively the size of the detected problems. This minimizes distrust in the tests. Programmers should believe in tests.

## Tradeoffs

### Pros

* Tests increase your confidence in the system, and improve your ability to change the functionality, the design and even the architecture of the system in a behavior-preserving way.

* Tests document how artifacts of a system are to be used. In contrast to written documentation, running tests are an always up-to-date description of the system.

* Selling testing to clients who are concerned by security and stability is not usually a problem. Assuring long term life of the system is also a good argument.
* Tests provide the necessary climate for enabling future system evolution.

* Simple unit testing frameworks exist for all the main object-oriented languages like Smalltalk, Java, C++ and even Perl.

## Cons

* Tests do not come for free. Resources must be allocated to write them.

* Tests can only demonstrate the presence of defects. It is impossible to test all the aspects of a legacy system (or any system, for that matter).

* Inadequate tests will give you false confidence. You may think your system is working well because all the tests run, but this might not be the case at all.

## Difficulties

* A plethora of testing approaches exists. Choose a simple approach that fits your development process.

* Testing legacy systems is difficult because they tend to be large and undocumented. Sometimes testing a part of a system requires a large and complex set-up procedure, which may seem prohibitive.

* Management may be reluctant to invest in testing. Here are some arguments in favor of testing:

- Testing helps to improve the safety of the system.

- Tests represent a tangible form of confidence in the system functionality.

- Debugging is easier when automated tests exist.

- Tests are simple documentation that is always in sync with the application.

* Developers may be reluctant to adopt testing. Build a business case to show them that tests will not only speed up today's development, but they will speed up future maintenance efforts. Once we discussed with a developer who spent one day fixing a bug and then three days more checking if the changes he made were valid. When we showed him that automated tests could help him in his daily work to debug his program more quickly, he was finally convinced.
* Testing can be boring for developers so at least use the right tools. For unit testing, SUnit and its many variants are simple, free and available for Smalltalk, C++, Java and other languages [BG98].

## Example

The following code illustrates a unit test written using JUnit in Java [BG98]. The test checks that the add operation defined on a class Money works as expected, namely that 12 CHF + 14 CHF = 26 CHF.

```java
public class MoneyTest extends TestCase {
    public void testSimpleAdd() {
        Money m12CHF= new Money(12, "CHF");          // (1)
        Money m14CHF= new Money(14, "CHF");
        Money expected= new Money(26, "CHF");
        Money result= m12CHF.add(m14CHF);              // (2)
        assert(result=currency().equalsegrue(n expected))
                && result.amount() == expected.amount()); // (3)
    }
}
```

This satisfies the properties that a test should have:

* This test is automated: It returns boolean value true if the action is the right one and false otherwise.

* It is stored: it is a method of a test class. So it can be versioned like any other code.

* It is repeatable: its initialization part (1) produces the context in which the test can be run and rerun indefinitely.

* It is independent of the other tests.

Using tests having these properties helps you to build a test suite for the long term. Every time you write a test, either after a bug fix or adding a new feature, or to test an already existing aspect of the system, you are adding *reproducible* and *verifiable* information about your system into your test suite. Especially in the context of reengineering a system this fact is important, because this reproducible and verifiable information can be checked after any change to see if aspects of a system are compromised.

## Rationale

Tests represent confidence in a system, because they specify how parts of the system work in a *verifiable* way, and because they can be run at any
<figure><img src="image_27.png" /><figcaption>Figure 6.2: Automated tests are the *foundation* for reengineering. They establish your confidence in the system, reduce risks, and improve confidence in your ability to change the system.</figcaption></figure>

time to check if the system is still consistent.

> "... testing simply exposes the presence of flaws in a program; it cannot be used to verify the absence of flaws. It can increase your confidence that a program is correct"

> — Alan Davis, Principle 111 [Dav95]

Systematic testing is heavily promoted by Extreme Programming [Bec00] one of the basic techniques necessary to be able to adapt programs quickly to changing requirements. Changing legacy systems is risky business. Will the code still work after a change? How many unexpected side-effects will appear? Having a set of automated, repeatable tests helps to reduce this risk.

* A set of running tests provides confidence in the system. ("Are you really sure this piece of code works?" "Yes, look, here I have the tests that prove it.")

* A set of running tests represents *reproducible* and *verifiable* information about your system, and is at all times in sync with the application. This in contrast to most of the written documentation, which is typically slightly outdated already the next day.

* Writing tests increases productivity, because bugs are found much earlier in the development process.

## Related Patterns

Write Tests to Enable Evolution is a prerequisite to Always Have a Running Version [p. 199]. Only with a comprehensive test program in place can you Migrate Systems Incrementally [p. 191].
Grow Your Test Base Incrementally and Test the Interface, Not the Implementation introduce a way to incrementally build a test suite while a system is evolving.
## 6.2 Grow Your Test Base Incrementally

**Intent** Balance the costs and the benefits of tests by incrementally introducing just the tests you need at a given point in time.

### Problem

When should you start to introduce tests? When can you stop?

*This problem is difficult because:*

* In a reengineering project, you cannot afford to spend too much time for writing tests.

* Legacy systems tend to be huge, so testing everything is impossible.

* Legacy systems tend to be poorly-documented and poorly-understood.

* The original developers may have left and the system maintainers may have only limited knowledge of the system's inner workings.

*Yet, solving this problem is feasible because:*

* We know where the fragile parts or the parts that we would like to change are.

* We could convince programmers that they can benefit from tests.

### Solution

Introduce tests incrementally for parts of the system you are working on.

### Hints

* Carefully assess your priorities and initially develop tests only for the most critical components. As you reengineer the system, introduce tests for the new features, parts of the legacy that may be affected, and any bugs you identify along the way.

* Keep a snapshot of the old system handy so you can later introduce tests that should run against both the original system and its new incarnation.
* Focus on business values. Start to write tests for the parts of your system that have the most important artifacts. Try to Record Business Rules as Tests.

* If you have the history of bug fixes or problems, apply Test Old Bugs [p. 316] as a starting point.

* If you have acceptable documentation and some original developers of the system at hand, consider applying Test Fuzzy Features [p. 316].

* Apply Test the Interface, Not the Implementation, start to test big abstractions and then refine tests if time allows. For example, if you have a pipeline architecture, start to write tests that ensure you that the output of the full pipeline is right given the right input. Then write tests for the individual pipeline components.

* Black-box test parts (subsystems, classes, methods) that are likely to change their implementation in the future.

## Tradeoffs

### Pros

* You save time by only developing the tests that you need.

* You build up a base of the most critical tests as the project progresses.

* You build confidence as you go along

* You streamline future development and maintenance activities.

### Cons

* You may guess wrong which aspects are critical to test.

* Tests can give you false confidence — untested bugs can still lurk in the system.

## Difficulties

* Setting-up the proper context for the tests may require considerable time and effort.

* Identifying the boundaries of the components to test is just hard. Deciding which parts to test and how fine-grained these tests should be, requires a good understanding of the system and the way you intend to reengineer it.
## Example

<figure><img src="image_28.png" /><figcaption>Figure 6.3: Introduce tests for the parts of the system you intend to change.</figcaption></figure>

Initially introduce tests only for the subsystems and component you intend to change. In Figure 6.3 we introduce some tests for subsystem ABC and for its component B. We apply Test the Interface, Not the Implementation to ensure that the tests for B should also pass for newB.

Note that if we only introduce tests for component B, then we fail to test its integration with A and C. In any case, it may be that we fail to test all important aspects, so it is important to incrementally add new tests as bugs are detected and repaired.

## Rationale

An incremental testing strategy allows you to start reengineering efforts before all the tests are in place. By focussing on just those tests that concern the parts of the system you are currently changing, you enable change with a minimal investment in testing, while help your team build confidence as you grow your tests base.

## Related Patterns

Use a Testing Framework to organize your tests.

Test the Interface, Not the Implementation provides a strategy for developing tests at arbitrary granularities. Record Business Rules as Tests provides another strategy for testing components that implement business
logic. Write Tests to Understand helps you prime a test base while you are
still reverse engineering the system.
## 6.3 Use a Testing Framework

**Intent** Encourage developers to write and use regression tests by providing a framework that makes it easy to develop, organize and run tests.

### Problem

How do you encourage your team to adopt systematic testing?

*This problem is difficult because:*

* Tests are boring to write.

* Tests may require a considerable test data to be built up and torn down.

* It may be hard to distinguish between test failures and unexpected errors.

*Yet, solving this problem is feasible because:*

* Most tests follow the same basic pattern: create some test data, perform some actions, see if the results match your expectations, clean up the test data.

* Very little infrastructure is needed to run tests and report failures and errors.

### Solution

Use a testing framework that allows suites of tests to be composed from individual test cases.

#### Steps

Unit testing frameworks, like JUnit and SUnit [BG98], and various commercial test harness packages are available for most programming languages. If a suitable testing framework is not available for the programming language you are using, you can easily brew your own according to the following principles:

* The user must provide test cases that set up test data, exercise them, and make assertions about the results
* The testing framework should wrap test cases as tests which can distinguish between assertion failures and unexpected errors.

* The framework should provide only minimal feedback if tests succeed.

- Assertion failures should indicate precisely which test failed.

- Errors should result in more detailed feedback (such as a full stack trace).

* The framework should allow tests to be composed as test suites.

## Tradeoffs

### Pros

* A testing framework simplifies the formulation of tests and encourages programmers to write tests and use them.

### Cons

* Testing requires commitment, discipline and support. You must convince your team of the need and benefits of disciplined testing, and you must integrate testing into your daily process. One way of supporting this discipline is to have one testing coach in your team; consider this when you appoint a Navigator [p. 25].

## Example

JUnit is a popular testing framework for Java, which considerable enhances the basic scheme described above. Figure 6.4 shows that the framework requires users to define their tests as subclasses of **TestCase**. Users must provide the methods **setup()**, **runTest()** and **tearDown()**. The default implementation of **setup()** and **tearDown()** are empty, and the default implementation of **runTest()** looks for and runs a method which is the name of the test (given in the constructor). These user-supplied hook methods are then called by the **runBare()** template method.

JUnit manages the reporting of failures and errors with the help of an additional **TestResult** class. In the design of JUnit, it is an instance of **TestResult** that actually runs the tests and logs errors or failures. In Figure 6.5 we see a scenario in which a **TestCase**, in its run method, passes
<figure><img src="image_29.png" /><figcaption>Figure 6.4: JUnit is a popular testing framework for Java that offers much more flexibility than the minimal scheme described above.</figcaption></figure>

control to an instance of TestResult, which in turn calls the runBare template method of the TestCase.

TestCase additionally provides a set of different kinds of standard assertion methods, such as assertEquals, assertFails, and so on. Each of these methods throws an AssertionFailedError, which can be distinguished from any other kind of exception.

In order to use the framework, we will typically define a new class, say TestHas table, that bundles a set of test suites for a given class, Has table, that we would like to test. The test class should extend j unit.framework.test.Case:

import junit.framework.*;

public class Test table extends TestC

The instance variables of the test class will hold the fixture - the actual test data:

private HashTable boss;

private String joe = "Joe";
<figure><img src="image_30.png" /><figcaption>Figure 6.5: In JUnit, tests are actually run by an instance of TestResult, which invokes the runBare template method of a TestCase. The user only needs to provide theattiUp() and runDown() methods, and the test method to be invoked by runTest().</figcaption></figure>

private String mary = "Mary";

private String dave = "Dave";

private String boris = "Boris";

There should be constructor that takes the name of a test case as its
parameter. Its behavior is defined by its superclass:

public TestASTEREVIEW name) {
    super(name);
}

TheattiUp() hook method can be overridden to set up the fixture. If
there is any cleanup activity to be performed, we should also override
tearDown(). Their default implementations are empty.

protected voidattiUp() {
    boss = new Tables();
}

We can then define any number of test cases that make use of the fix-
ture. Note that each test case is independent, and will have a fresh copy of
the fixture. (In principle, we should design tests that not only exercise the
entire interface, but the test data should cover both typical and boundary
cases. The sample tests shown here are far from complete.)
Each test case should start with the characters "test":

```java
public void testEmpty() {
    assert(boss.isEmpty());
    assertEquals(boss.size(), 0);
    assert(!boss.contains(joe));
    assert(!boss containsKey(joe));
}

public void testBasics() {
    boss.put(joe, mary);
    boss.put(mary, dave);
    boss.put(boris, dave);
    assert(!boss.isEmpty());
    assertEquals(boss.size(), 3);
    assert(boss contains(mary));
    assert(!boss contains(joe));
    assert(boss containsKey(mary));
    assert(!boss containsKey(dave));
    assertEquals(boss.get(joe), mary);
    assertEquals(boss.get(mary), dave);
    assertEquals(boss.get(dave), null);
}
```

You may provide a static method `suite()` which will build an instance of `junit.framework.TestSuite` from the test cases defined by this class:

```java
public static TestSuite suite() {
    TestSuite suite = new TestSuite();
    suite.addTest(new TestZOгиable("testBasics"));
    suite.addTest(new TestZOгиable("testEmpty"));
    return suite;
}
```

The test case class should be compiled, together with any class it depends on.

To run the tests, we can start up any one of a number of *test runner* classes provided by the JUnit framework, for instance `junit.ui.TestRunner` (see Figure 6.6).

This particular test runner expects you to type in the name of the test class. You may then *run* the tests defined by this class. The test runner will look for the suite method and use it to build an instance of TestSuite. If you do not provide a static suite method, the test runner will automatically build a test suite assuming that all the methods named test* are test cases. The test runner then runs the resulting test suite. The interface will report
<figure><img src="image_31.png" /><figcaption>Figure 6.6: An instance of java.ui.TestRunner.</figcaption></figure>

<figure><img src="image_32.png" /><figcaption>Figure 6.7: A successful test run.</figcaption></figure>
how many tests succeeded (see Figure 6.7). A successful test run will show a green display. If any individual test fails, the display will be red, and details of the test case leading to the failure will be given.

## Rationale

A testing framework makes it easier to organize and run tests.

Hierarchically organizing tests makes it easier to run just the tests that concern the part of the system you are working on.

## Known Uses

Testing frameworks exist for a vast number of languages, including Ada, ANT, C, C++, Delphi, .Net (all languages), Eiffel, Forte 4GL, GemStone/S, Jade, JUnit Java, JavaScript, k language (ksql, from kbd), Objective C, Open Road (CA), Oracle, PalmUnit, Perl,.phpUnit, PowerBuilder, Python, Rebol, 'Ruby, Smalltalk, Visual Objects and UVisual Basic.

Beck and Gamma give a good overview in the context of JUnit [BG98].
## 6.4 Test the Interface, Not the Implementation

*Also Known As: Black-Box Testing [Pre94]*

**Intent** Build up reusable tests that focus on external behavior rather than on implementation details, and thereby will survive changes to the system.

### Problem

How can you develop tests that not only protect your software legacy, but also will continue to be valuable as the system changes?

*This problem is difficult because:*

* Legacy systems have many features that should continue to function as the system evolves.

* You cannot afford to spend too much time writing tests while reengineering the system.

* You do not want to waste effort in developing tests that will have to be changed as you change the system.

*Yet, solving this problem is feasible because:*

* The interfaces to the components of the system tell you what should be tested.

* Interfaces tend to be more stable than implementations.

### Solution

Develop black-box tests that exercise the public interface of your components.

### Hints

* Be sure to exercise boundary values (i.e., minimum and maximum values for method parameters). The most common errors occur here.

* Use a top-down strategy to develop black-box tests if there are many fine-grained components that you do not initially have time to develop tests for.

* Use a bottom-up strategy if you are replacing functionality in a very focussed part of the legacy system.
# Tradeoffs

## Pros

* Tests that exercise public interfaces are more likely to be reusable if the implementation changes.

* Black-box tests can often be used to exercise multiple implementations of the same interface.

* It is relatively easy to develop tests based on a component's interface.

* Focusing on the external behavior reduces considerably the possible tests to be written while still covering the essential aspects of a system.

## Cons

* Back-box tests will not necessarily exercise all possible program paths. You may have to use a separate coverage tool to check whether your tests cover all the code.

* If the interface to a component changes you will still have to adapt the tests.

## Difficulties

* Sometimes the class does not provide the right interface to support black-box testing. Adding accessors to sample the state of the object can be a simple solution, but this generally weakens encapsulation and makes the object less of a black box.

## Example

Let's look back at the test presented in Write Tests to Enable Evolution. The code we saw earlier was supposed to check whether the add operation defined on a class *Money* works as expected. However, we see that the assert in line (3) actually depends on the internal implementation of the *Money* class, because it checks for equality by accessing the parts of equality.

```java
public class MoneyTest extends TestCase {
    // ...
    public void testSimpleAdd() {
        Money m12CHF= new Money(12, "CHF"); // (1)
```
```java
Money m14CHF= new Money(14, "CHF");
Money expected= new Money(26, "CHF");
Money result= m12CHF.add(m14CHF); // (2)
assert(result=currency().equalsieraugu))
    && result.amount() == expected.amount()); // (3)
}
}
```

However, if the class `Money` would override the default `equals` operation defined on `Object` (doing so would also require us to override `hash-Code`), the last assert statement could be simplified and would become independent of the internal implementation.

```java
public class MoneyTest extends TestCase {
    // ...
    public void testSimpleAdd() {
        Money m12CHF= new Money(12, "CHF"); // (1)
        Money m14CHF= new Money(14, "CHF");
        Money expected= new Money(26, "CHF");
        Money result= m12CHF.add(m14CHF); // (2)
        assertiezpected.equals(result)); // (3)
    }
}
```

## Rationale

The interface of a component is a direct consequence of its collaborations with other components. Black-box tests therefore have a good chance of exercising the most important interactions of a system.

Since interfaces tend to be more stable than implementations, black-box tests have a good chance of surviving major changes to the system, and they thereby protect your investment in developing tests.

## Known Uses

Black-Box testing is a standard testing strategy [Som96].

## Related Patterns

Record Business Rules as Tests adopts a different strategy to developing tests which focuses on exercising business rules. This is fine if the components to be tested are the ones that implement the business logic. For most
other components, Test the Interface, Not the Implementation will likely be more appropriate.

Components that implement complex algorithms may not be well-suited to black-box testing, since an analysis of the interface alone may not reveal all the cases that the algorithm should handle. White-box testing [Som96] is another standard technique for testing algorithms in which test cases are generated to cover all possible paths through an algorithm.
## 6.5 Record Business Rules as Tests

**Intent** Keep the system in sync with the business rules it implements by encoding the rules explicitly as tests.

### Problem

How do you keep the *actual business rules*, the *documentation* about those business rules and the *system implementation* in sync, while all three are changing?

*This problem is difficult because:*

* Written documentation gets out of date quickly and does not ensure you that your system really implements the description of the business rules you have.

* Business rules tend to be implicit in the code. It may not be obvious which pieces of software are responsible for computing a given business rule.

* Developer turn-over introduces a high risk for your business by having more and more people knowing less and less about the system.

* Most of the time only one programmer or user knows specific rules, and that person could be leaving tomorrow.

* Business rules are likely to change due to external factors, such as the introduction of a new law, so it is important to represent them explicitly.

*Yet, solving this problem is feasible because:*

* Most business rules are well expressed by sets of canonical examples, each of which requires certain well-defined actions to be taken, and results in some clear, observable results.

### Solution

Write executable tests that record the business rules as test cases, actions, and tests over the results. When tests break, you know that things are out of sync.
## Hints

* Developers and clients can write tests. Developers may write tests associated with specific functionality or piece of code. User may also have to write integration tests in the form of use cases that bind together several unit tests [Dav95] [Bec00].

* Note that you are not interested in the implementation strategies or optimization aspects, but only the business rules.

## Tradeoffs

### Pros

* The rules become explicit, thereby reducing dependency on human memory.

* You need to record the business rules anyway before you can reengineer the legacy system.

* Recording business rules as tests enables evolution: when new features must be added, you can check that the existing business rules are still correctly implemented by running the regression tests. On the other hand, when the business rules change, you can update the corresponding tests to reflect the changes.

### Cons

* Tests can only encode concrete scenarios, not actual the logic of the business rules themselves.

* When the business logic must deal with an extremely large number of cases, it may be impractical to test them all.

## Difficulties

* Recording business rules does not mean extracting them. Extracting business rules from code with the current technology is a pipe dream.

* Recording business rules can be difficult for system whose original developers and users have all left.
Examples

In this example we compute the amount of additional money an employee receives for a child. The rule states that a person or couple gets an amount of money for every child he, she or they raise. Basically parents get CHF 150,- per month for every child younger than 12 years, and CHF 180,- for every child between 12 and 18 and for every child between 18 and 25 as long as the child is not working and is still in the educational system. A single parent gets the full 100% of this money as long as he or she is working more than 50%. Couples get a percentage of the money that is equal to the summed working percentages of both partners.

The following Smalltalk code shows a test that hardcodes the ex-
pected outcomes for the different computations. It allows for automati-
cally checking the outcomes instead of having to print the outcomes and
check by hand if they are right, and it acts as a regression test. Secondly it
documents the expected outcome of the different computations.

testMoneyGivenForKids

singlePerson80occupationWithOneKidOf5
couplePerson40occupationWithOneKidOf5
couplePerson100occupationWith2KsidOf5
couplePersonWithOneKidOf14

"cases are extracted from a database after the system has
performed the computation"

singlePerson80WithOneKidOf5 := extract....

couplePerson40occupationWithOneKidOf5 := extract....

couplePerson100occupationWithOneKidOf5 := extract....

couplePersonWithOneKidOf14 := extract....

"tests"

"We test that the right amount of money is computed correctly"

self assert: singlePerson80occupationWithOneKidOf5 moneyForKid =
150.

self assert: couplePerson40occupationWithOneKidOf5 moneyForKid =
150*4.

self assert: couplePerson100occupationWith2KidsOf5 moneyForKid =
150*2.

self assert: couplePersonWithOneKidOf14 moneyForKid = 180.
## Rationale

Tests are a good way to document what the system does. By documenting business rules as tests, you guarantee that the description of the business rules will be in sync with the implementation.

The beginning of a reengineering project is a good point in time to set up a process to document knowledge about the system as explicit tests.

## Related Patterns

While you are reverse engineering a legacy system, you may Write Tests to Understand. During this process it will be natural to Record Business Rules as Tests. In this way you can prime your test base as you Grow Your Test Base Incrementally.
## 6.6 Write Tests to Understand

**Intent** Record your understanding of a piece of code in the form of executable tests, thus setting the stage for future changes.

### Problem

How do you develop an understanding of a part of a legacy system which contains neither tests nor accurate and precise documentation?

*This problem is difficult because:*

* Code is always difficult to understand.

* You would like to make hypotheses about what the code is really doing and validate them.

* You would like to specify as precisely as possible the behavior of the system.

* You would like to record your understanding to communicate it but you do not want to waste your time in writing documents that will be obsolete as soon as you start changing the code.

*Yet, solving this problem is feasible because:*

* The piece of code is relatively small and has clearly defined boundaries.

* You have the possibility to specify tests and validate them.

### Solution

Encode your hypotheses and conclusions as executable tests.

### Tradeoffs

#### Pros

* Tests help you to validate your understanding.

* Tests can provide a precise specification of certain aspects of the system. Tests cannot be fuzzy.
* Tests can be applied to gain different levels of understanding. For example, black-box tests can help you to refine your understanding of roles and collaborations, whereas white-box tests can help you to gain understanding of the implementation of complex logic.

* The tests that you develop will help to enable future reengineering effort.

* Tests will force you to be precise about the creation and the use of the objects under test.

## Cons

* Writing tests is time consuming.

## Difficulties

* Obtaining a well defined context in which you can test the objects is difficult especially if the objects to be tested do not represent specific abstractions. Looking for the places where objects you want to understand are created can help.

* Concurrent systems are known to be difficult to test, so tests can miss important aspects (such as handling of race conditions).

## Rationale

By writing automated tests, you exercise parts of the system you want to understand, while recording your understanding and setting the stage for future reengineering effort.

## Related Patterns

Before writing any tests, you might want to Refactor to Understand [p. 127]. As you write your tests, be sure to Tie Code and Questions [p. 121].
# Chapter 7

## Migration Strategies

Your reengineering project is well underway. You have developed a good understanding of the legacy system and you have started to Write Tests to Enable Evolution [p. 153]. You have gone through a process of Setting Direction and have decided to tackle the Most Valuable First [p. 29].

How can you be sure that the new system will be accepted by users? How do you migrate to the new system while the old system is being used? How can you test and evaluate the new system before it is finished?

### Forces

* Big-bang migration carries a high risk of failure.

* Introducing too many changes at once may alienate users.

* Constant feedback helps you stay on track, though it may be difficult and costly to achieve.

* Users have to get their work done; they don't want to be distracted by incomplete solution.

* Legacy data must survive while the system is being used.

### Overview

It is not enough to reengineer a legacy system and then deploy it. In fact, if you try this, you will surely fail (for the same reasons that big Waterfall projects in new territories often fail). You must be prepared to introduce the new solution gradually, to gain the confidence and collaboration
<figure><img src="image_33.png" /><figcaption>Figure 7.1: How, why and whither to migrate legacy systems.</figcaption></figure>

of the users, and you must adopt a strategy for migrating gradually and painlessly from the existing system, while it is still being deployed, to the new system.

The central message of this cluster is to Migrate Systems Incrementally. This is, however, easier said than done. In figure 25 we can see that in order to Migrate Systems Incrementally, we should consider a large number of other patterns. Since there exists a vast literature on system migration, we do not attempt to cover the topic in great detail. We have selected, however, the patterns that we consider to be most important for reengineering object-oriented legacy systems, and summarized the main points. Where appropriate, we point the reader to further sources of information.

Although the central pattern of this cluster is Migrate Systems Incrementally, the key motivation is provided by Involve the Users and Build Confidence. These first three patterns are fundamental patterns for minimizing risk and increasing the chances of success:

* Involve the Users increases the chance that users will accept the new system by involving them closely in the entire reengineering process, getting them to use intermediate results, and providing them with strong support. It is easier to achieve if you Migrate Systems Incrementally and Build Confidence step by step.

* Build Confidence helps you overcome skepticism and doubt by reg-
ularly delivering results that are of value to the users.

* Migrate Systems Incrementally recommends that the old system be gradually and incrementally replaced by the new system. New results can then be integrated as you proceed, thus helping you to Build Confidence and Involve the Users.

It is very hard to Migrate Systems Incrementally unless you also adhere to the following practices:

* Prototype the Target Solution to test the new architecture and new technical risks. It is too easy to be tempted to think you don't need a prototype since you already have a running system, but this is almost always a mistake.

* Always Have a Running Version helps to keep changes in sync by ensuring that they are integrated frequently.

* Regression Test After Every Change helps to you Always Have a Running Version by making sure that everything that used to run still runs. It presupposes that you Write Tests to Enable Evolution [p. 153].

Depending on the circumstances, there are various practices that may help you to Migrate Systems Incrementally:

* Make a Bridge to the New Town introduces the metaphor of a (data) "bridge" to allow you to gradually migrate data from a legacy component to its replacement, while the two run in tandem. When all the data have been transferred, the legacy component can be retired.

* Present the Right Interface helps you to develop the target system in increments by wrapping the old functionality to export the abstractions you really want.

* Distinguish Public from Published Interface distinguishes between stable (public) and unstable (published) interfaces to facilitate parallel development within a reengineering team.

* Deprecate Obsolete Interfaces lets you gracefully retire obsolete interfaces without immediately invalidating clients.

Finally, the following two practices may help you avoid making radical, but unnecessary changes:

* Conserve Familiarity warns you against introducing radical interface changes that may alienate users.
* Use Profiler Before Optimizing [p. 221] reminds you to delay considering performance issues until you can demonstrate that you have a problem and can pinpoint the source of the problem.
## 7.1 Involve the Users

*Also Known As: Engage Customers [Cop95]*

**Intent** Maximize acceptance of changes by involving the users at every step.

### Problem

How can you be sure that users will accept the reengineered system?

*This problem is difficult because:*

* The old systems works. It is clunky, but the users know how it works and know how to get around the problems.

* People hate to have to learn something new unless it really makes their life simpler.

* User perceptions of what is needed to improve a system tend to change as the system evolves.

* Users can have difficulty evaluating a paper design.

* It is hard to get excited about a new system that is not ready to use.

*Yet, solving this problem is feasible because:*

* Users will try new solutions if they see that their needs are being seriously addressed.

* Users will give you feedback if you give them something useful to use.

### Solution

Get the users directly involved in the new development, and support them closely in using the new system.

#### Steps

Get the users to tell you where their priorities lie. Start with Most Valuable First [p. 29]. Break the priorities down into small steps that can be delivered in regular increments, so you can Build Confidence [p. 189].
Create an environment that will encourage contact between users and developers. Physical location is important.

Establish simple procedures for delivering intermediate results on a regular basis and obtaining feedback. Early prototypes may help, especially to evaluate risky new technologies or approaches. A good strategy is to Migrate Systems Incrementally [p. 191] so that users can start using the new system as it is being built. You should Conserve Familiarity [p. 219] to avoid alienating users.

## Tradeoffs

### Pros

* Requirements will continuously be validated and updated, increasing your chances that you will move in the right direction.

* If the users feel they are getting useful results and they are being supported, they will put extra effort into giving useful feedback.

* Users will be involved throughout the effort, eliminating the need for a special training session late in the project.

### Cons

* Developers may feel that supporting users is distracting them from the job of reengineering the system.

* If you succeed in involving the users, this will raise expectations and put extra pressure on your team. For instance, Yourdon mentions that prototypes can really raise expectations too much and that you should always make clear which parts are not yet working [You97].

### Difficulties

* It can be hard to involve the users initially, before you have shown any results.

* You can't involve everybody, and the users who are left out might feel neglected.

## Rationale

You need a feedback loop to ensure that you are addressing the real customer needs. By involving and supporting the users, you encourage this
feedback loop.

Coplien points out: "Note that 'maintaining product quality' is not the problem being solved here. Product quality is only one component of customer satisfaction." [Cop95]

## Related Patterns

Virtually all of the patterns in this cluster support Involve the Users. Mitrate Systems Incrementally to get the users working with the system as it is being reengineered and thereby Build Confidence.

The Planning Game [BF01] is an effective technique to Involve the Users by iteratively identifying stories, estimating costs, and committing to the stories to be released.
## 7.2 Build Confidence

**Intent** Improve your chances of overall success by demonstrating results in regular increments.

### Problem

How can you overcome the high degree of skepticism that customers and team members often have for any kind of software project?

*This problem is difficult because:*

* Few software projects meet requirements, come in on time, and stay within budget. The skepticism that accompanies most projects can easily lead to defeatism, and projects can fail as a self-fulfilling prophecy.

* Users rarely get what they really want or need.

* It can be hard to convince either the users or even your own team that the legacy system can really be salvaged.

*Yet, solving this problem is feasible because:*

* You don't need to solve all the problems at once.

### Solution

Create a positive atmosphere by demonstrating some positive results as early as you can, and continue to do so on a regular basis.

### Steps

Pick short intervals for delivering new results. At each step, try to agree together with the users what are the smallest results that can demonstrate real value.

### Tradeoffs

#### Pros

* Both users and developers can measure real progress.

* It is easier to estimate the cost of smaller steps.
## Cons

* It takes time to frequently synchronize with the users.

* Users may resent the extra work it takes to use the new system in tandem with the old one.

* If you succeed to demonstrate good results early in the project, you may raise expectations too high.

## Difficulties

* Some requirements can be hard to break down into small steps, particularly if they entail architectural changes to the system.

* Reengineering teams must be careful not to alienate the developers of the original system, since they are one of the most valuable sources of information.

* It is not enough to convince users — you must also take care to get commitment from management. It is hard to convince management in small steps. Plan big demos at regular intervals.

## Rationale

By taking smaller steps, you reduce the risk that an individual step will fail. Frequent, positive results help to build confidence. By the same token, Extreme Programming advocates Small Releases [Bec00]. Even negative results help you to monitor progress and understand better the situation, and so help to build up confidence.

## Related Patterns

Prototype the Target Solution and Make a Bridge to the New Town can make it easier to demonstrate results in small steps.

It is easier to Build Confidence if you Involve the Users.
## 7.3 Migrate Systems Incrementally

*Also Known As: Chicken Little [BS95]*

**Intent** Avoid complexity and risk of big-bang reengineering by deploying functionality in frequent increments.

### Problem

When should you plan to deploy the new system?

*This problem is difficult because:*

* Projects are often planned and funded on large time scales, with "big bang" requirements specification done up front.

* The real requirements are often only clear in hindsight. Users will resist adopting a new system that is radically different from what they are used to, especially if it does not work flawlessly from the beginning.

* The longer you wait to deploy the new system, the longer you must wait to get user feedback.

* You cannot deploy an incomplete system. Users do not have time to waste on incomplete solutions.

*Yet, solving this problem is feasible because:*

* You have a running system that can be extended and modified.

### Solution

Deploy a first *update* of the legacy system as soon as you can, and migrate incrementally to the target system.

#### Steps

* Decompose the legacy system into parts.

* Choose one part to tackle at a time.

* Put tests in place for that part and the parts that depend on it.
* Take appropriate steps to wrap, reengineer or replace the legacy component.

* Deploy the updated component and obtain feedback.

* Iterate.

## Tradeoffs

### Pros

* You get user feedback early and Build Confidence.

* You see immediately when things break.

* Users learn the new system as it's being built.

* The system is always deployed.

* The system is always being tested, so you can't skip testing.

### Cons

* You will have to work harder to keep the system running while you are changing it.

### Difficulties

* It can be difficult to migrate to a new architecture. You may want to Prototype the Target Solution to get the new architecture in place, and Present the Right Interface to the old system to hide the legacy interfaces while you migrate the underlying components.

* It is risky to change a running system. Be sure to Regression Test After Every Change.

## Rationale

You get the best user feedback from a running system. Users are more motivated and involved with a system they use daily.

## Known Uses

*Migrating Legacy Systems* [BS95] introduces this pattern under the name "Chicken Little" (to migrate incrementally means to "take Chicken Little
steps"). This book discusses in great detail strategies and techniques for incremental migration.

## Related Patterns

Apply Most Valuable First [p. 29] to select the legacy components to work on first. Appoint a Navigator [p. 25] to maintain architectural integrity.

Write Tests to Enable Evolution [p. 153], and Grow Your Test Base Incrementally [p. 159] as you migrate. Be sure to Test the Interface, Not the Implementation [p. 171] so you do not always have to rewrite your tests as you reengineer or replace legacy components. Regression Test After Every Change [p. 201] so you can Always Have a Running Version [p. 199].

Consider applying Present the Right Interface for legacy components that you do not intend to reengineer or replace.

You might consider to Make a Bridge to the New Town [p. 203] if you need to migrate data from legacy components that you are replacing.
## 7.4 Prototype the Target Solution

**Intent** Evaluate the risk of migrating to a new target solution by building a prototype.

### Problem

How do you know if your ideas for the new target system will work?

*This problem is difficult because:*

* It is risky to make radical changes to a working system.

* It can be hard to anticipate how design changes will impact existing functionality.

* A solution that works is more believable than one that has not been tested.

*Yet, solving this problem is feasible because:*

* You don't need to reengineer the whole legacy system to test the new ideas.

### Solution

Develop a prototype of the new concept and evaluate it with respect to the new, emerging requirements.

### Steps

* Identify the biggest technical risks for your reengineering project. Typically they will concern things like:

- choice of a new system architecture

- migration of legacy data to new system

- adequate performance — or performance gains — with new technology or platform (for example, demonstrating that a certain transaction throughput can be achieved)

* Decide whether to implement an exploratory (i.e., throwaway) prototype that will service purely to evaluate the feasibility of a technical option, or rather an evolutionary prototype that will eventually evolve into the new target system.
- An exploratory prototype must be designed to answer very precise questions. These may be purely technical questions, such as whether the new platform can meet performance constraints set by the legacy system, or they may be usability questions which require participation of and evaluation by the users. The exploratory prototype does not need to be designed to address any other issues or questions, and will not be part of the migrated system (although the answers it provides will influence the new system).

- An evolutionary prototype, on the other hand, is intended to eventually replace a legacy component, and must therefore reflect the target architecture. The new architecture most not only adequately support the legacy services, but also overcome the obstacles that limit the legacy solution's usefulness. The prototype must be designed to answer these risks first.

## Tradeoffs

### Pros

* A prototype can be built quickly, since it does not have to implement all the functionality of the legacy system.

* You can hack parts of the legacy system to get your prototype running.

* You can learn quickly if your ideas for the target system are sound.

### Cons

* Users may not be highly motivated to spend a lot of time evaluating a throwaway prototype.

* You may be tempted to continue to develop the throwaway prototype.

### Difficulties

* It may be hard to convince yourself or your customer of the need for a prototype — after all, you already have a running system.

* It can take too much time to get an evolutionary prototype up to speed. Consider applying Present the Right Interface to legacy components to provide a good interface for legacy services to the prototype.
## Rationale

A prototype can tell you quickly whether a certain technical approach is sound or not. Brooks in *The Mythical Man-Month* [Bro75] advises us to "write one to throw away" since it is hard to get it right the first time.

Love [Lov93] takes this one step further and warns us that, for object-oriented systems we should "write two to throw away"! Foote and Yoder [FY00] argue that, among other things, Throwaway Code is often the best way to clarify domain requirements, but they also warn that a prototype risks evolving into a "Big Ball of Mud".

## Related Patterns

You might consider applying Make a Bridge to the New Town to migrate legacy data to an evolutionary prototype.
## 7.5 Always Have a Running Version

**Intent** Increase confidence in changes by regularly rebuilding the system.

### Problem

How do you convince your customer that you are on the right path?

*This problem is difficult because:*

* It can be hard to demo a software system under development, or to discuss problems with users since there is often no stable, running version of the system available.

* Integrating changes from multiple versions of a system can be slow and painful.

*Yet, solving this problem is feasible because:*

* You don't have to wait until a component is "finished" before integrating it.

### Solution

Institute a discipline of integrating new changes and developments on a daily basis.

#### Steps

* Have version management and configuration management systems in place.

* Make sure you have regression tests in place for the parts you are working on.

* Institute a discipline of short transactions for checking out system components and checking them back in again. Plan iterations to be as short as possible to allow changes to be integrated into a running system.
# Tradeoffs

## Pros

* You always have a working version to demo.

* You can always have a working version to run your regression tests.

* You can quickly validate your changes, thereby helping you to Build Confidence.

## Cons

* You must continuously integrate changes.

## Difficulties

* Large systems may have very long build times. You may need to rearchitect the system first to enable shorter build times.

* It can be hard to break some kinds of large modifications into meaningful updates that can be individually integrated.

# Rationale

Many practitioners advocate a process of continuous integration as a way to avoid a risky and painful big-bang integration [Boo94].

# Related Patterns

Regression Test After Every Change minimizes the risk of defects creeping in during integration.

Continuous Integration [Boo94] [Bec00] is a proven way to Always Have a Running Version.
## 7.6 Regression Test After Every Change

**Intent** Build confidence by making sure that whatever worked before still works.

### Problem

How can you be sure that the last change you made won't break the system?

*This problem is difficult because:*

* In a complex system, small changes can have unexpected side effects. A seemingly innocuous change may break something without this being immediately discovered.

*Yet, solving this problem is feasible because:*

* You have written test suites that express how the system should behave.

### Solution

Run your regression test suite every time you think you have reached a stable state.

### Tradeoffs

#### Pros

* It is easier to Always Have a Running Version.

* It is easier to Build Confidence as you proceed.

#### Cons

* You must relentlessly write the tests.

#### Difficulties

* The legacy system may not have adequate regression tests defined. To enable evolution, you will have to Grow Your Test Base Incrementally [p. 159]
* Tests can only show that defects are present, not that they are absent. You may have failed to test precisely the aspect that you have broken.

* Run the tests may be very time-consuming, so you might want to run only those tests that you think might be affected by your change. Categorize your tests to avoid "ad hoc" testing of changes, but run all the tests at least once a day.

## Rationale

Regression tests tell you that whatever ran before still runs. If you consistently build up tests for defects you discover and new features, you will end up with a reusable test base that gives you confidence that your changes are sound, and helps you detect problems earlier.

Davis advocates "Regression Test After Every Change" [Dav95] as standard Software Development practice.

## Related Patterns

You should have already started to Write Tests to Enable Evolution [p. 153].

A common practice in Extreme Programming is to write tests *before* you implement new functionality [JAH01]. In the context of reengineering, you should consider writing tests that will fail before you make a change, and will pass if the change is correctly implemented. (Unfortunately it is not generally possible to design tests that will *only* pass if the change is correct!)

Regression tests should help you to Retest Persistent Problems [p. 316].
## 7.7 Make a Bridge to the New Town

*Also Known As:* The Bridge to the New Town [Kel00], Keep the Data — Toss the Code [BS95]

**Intent** Migrate data from a legacy system by running the new system in parallel, with a bridge in between.

### Problem

How do you incrementally migrate data from a legacy system to its replacement while the two systems are running in tandem?

*This problem is difficult because:*

* Some components of the legacy system are beyond repair and should be replaced.

* Big-bang replacement of critical components is highly risky.

* The *data* manipulated by the legacy components must be kept available and alive during the migration.

*Yet, solving this problem is feasible because:*

* You have a running legacy system.

### Solution

Make a (data) bridge that will incrementally transfer data from the legacy system to the replacement system as new components are ready to take the data over from their legacy counterparts.

#### Steps

* Identify legacy and replacement components that deal with the same logical data entities.

* Implement a "data bridge" which is responsible for redirecting *read* requests from the new component to the legacy data source, if the data have not already been migrated. The bridge is responsible for any necessary data conversion. The new component should not be aware of the bridge.
<figure><img src="image_34.png" /><figcaption>Figure 7.2: A Bridge helps you to transparently transfer data to the new system.</figcaption></figure>

* Adapt the legacy component to redirect *write* requests to the new component, so that the new data stay up-to-date.

* When all the data have been transferred, remove the bridge and the legacy component.

## Tradeoffs

### Pros

* You can start using the new system without migrating all the legacy data.

### Cons

* A data bridge can be tricky to implement correctly if there is not a simple mapping between the legacy data and the new data.

* Once some of the data has been transferred, it can be hard to go back.

* The data bridge will add a performance overhead which may or may not be acceptable.

### Difficulties

* "Stepwise migration schemes have proven very effective in large, layered business systems. They are not common in let's say CAD applications that have check in /check out persistence and a tightly coupled and very woven object net." [Kel00]
## Known Uses

Brodie & Stonebraker discuss much more thoroughly the use of data bridges and gateways in *Migrating Legacy Systems* [BS95].

Keller in "The Bridge to the New Town" [Kel00] focusses more on the technical issue of migrating legacy data, and he points out numerous examples of the pattern successfully being applied.

There are many possible variants of this pattern, depending on whether the entire legacy system is to be replaced, or only a component, and whether users should be able to have access to both systems at the same time or not.

## Rationale

A bridge between the old and new systems allows you to let users start using features of the new system before it is complete. The bridge isolates the two systems from each other so that the new system can be developed according to a new architectural vision without influence from the legacy system.

## Related Patterns

A bridge helps you Migrate Systems Incrementally and thereby Build Confidence.
## 7.8 Present the Right Interface

*Also Known As:* Semantic Wrapper [O'C00], Sweeping it Under the Rug [FY00]

**Intent** Wrap a legacy system to export the right abstractions, even if they are not reflected in the existing implementation.

### Problem

How should the new target system access legacy services during the migration process?

*This problem is difficult because:*

* The target system is not yet complete so you must rely on legacy services during the migration.

* The legacy system does not present the interfaces you need for the target system.

* Implementing new components directly in terms of legacy components will bias the target towards the legacy architecture and design.

*Yet, solving this problem is feasible because:*

* You don't have to access the legacy services directly.

### Solution

Identify the abstractions that you want to have in the new system, and wrap up the old software to emulate the new abstractions.

### Hints

Consider, for example, a procedural graphics library that will be used within an object-oriented system. It will be too costly and time-consuming to reimplement the library in an object-oriented way. It would be easier to wrap it as a utility class (i.e., as a class with static methods but no instances), but it would be wiser to write a slightly thicker wrapper that presents a truly object-oriented interface, but is implemented using the underlying procedural abstractions. In this way the new system will not be polluted by legacy abstractions.
# Tradeoffs

## Pros

* It is easier to wean the target system from legacy services if they can use appropriate abstractions from the start.

* You reduce the risk that the legacy design will adversely influence the new target.

## Cons

* The new interface may not be stable, so developers may be reluctant to use it.

## Difficulties

* It can be hard to resist the temptation to simply wrap the procedural abstractions as utility classes.

## Known Uses

Alan O'Callaghan [O'C00] presents this pattern as "Semantic Wrapper" briefly in the context of the ADAPTOR pattern language, which addresses migration of large-scale business-critical legacy systems to object-oriented and component-based technology.

## Rationale

Present the Right Interface frees you from thinking in terms of the legacy design and makes it easier to consider alternative approaches.

## Related Patterns

Present the Right Interface superficially resembles an Adapter [p. 319], since both use wrappers as their implementation technique. An Adapter, however, adapts an incompatible interfaces to another interface expected by its clients. Present the Right Interface, on the other hand, introduces a new, more suitable interface to a legacy component.

Be sure to Deprecate Obsolete Interfaces.
If the new interface implemented by the Present the Right Interface is
not stable, you should Distinguish Public from Published Interface.
## 7.9 Distinguish Public from Published Interface

*Also Known As: Published Interface [O'C00]*

**Intent** Facilitate parallel development by distinguishing unstable "published interfaces" from stable "public interfaces".

### Problem

How do you enable migration from legacy interfaces to new target interfaces while the new interfaces are still under development?

*This problem is difficult because:*

* You want to enable migration to the new target system as early as possible.

* You do not want to freeze the interfaces of new target components too early.

* Changing the interface to a component that is widely used will slow down development.

*Yet, solving this problem is feasible because:*

* You can control the status of the interfaces you provide.

### Solution

Distinguish between public interfaces of components that are available to the rest of the system, and unstable "published" interfaces of components that are available within a subsystem, but are not yet ready for prime time.

### Hints

Since "published" interfaces are not supported by any programming language, you may have to use naming conventions, or abuse other features to achieve the desired effect.

* In Java, consider declaring such interfaces as **protected**, or giving them package scope (undeclared). When the interfaces stabilize, you may redeclare them as being **public**.
* In C++, consider declaring components with published interfaces private or protected, and declare as friends the clients that are permitted to use them. When the interfaces stabilize, redeclare the components as public, and delete the declarations of friends.

* In Smalltalk, consider declaring categories of published components. Also consider declaring published message categories to distinguish stable and unstable messages.

* Consider decorating the names of unstable components or interfaces to indicate their “published” status. When the component becomes public, rename it and patch all its clients or deprecate the version with the old name (Deprecate Obsolete Interfaces).

## Tradeoffs

### Pros

* Clients of published interfaces are aware that they are likely to change.

### Cons

* Identifying an interface as “published” is purely a matter of convention and discipline.

* Promoting an interface from published to public entails a certain overhead for clients who should upgrade to the new interface.

### Difficulties

* Clients can be put in a bind: should they use an unstable published interface, or continue to use the legacy service?

## Known Uses

Published Interface is another pattern of the ADAPTOR pattern language [O'C00].

## Rationale

Clients are in a better position to evaluate the risk of using a component if they know its interface is declared to be “published” but not yet public.
**Related Patterns**

When you Present the Right Interface to a legacy component, the new in-
terface may not be stable, so be careful to Distinguish Public from Pub-
lished Interface. When the new interface stabilizes, or is substituted by a
stable replacement component, the interface may become public.

Upgrading an interface to public may entail a change to the way it is
accessed. Be sure to Deprecate Obsolete Interfaces.
## 7.10 Deprecate Obsolete Interfaces

*Also Known As: Deprecation [SP98]*

**Intent** Give clients time to react to changes to public interfaces by flagging obsolete interfaces as "deprecated".

### Problem

How do you modify an interface without invalidating all the clients?

*This problem is difficult because:*

* Changing a public interface can break many clients.

* Leaving an obsolete interface in place will make future maintenance more difficult.

* Not all changes are for the better.

Yet, solving this problem is feasible because:

* The old and the new interfaces can coexist for a period of time.

### Solution

Flag the old interface as being "deprecated", thereby notifying clients that it will almost certainly be removed in the next upcoming release.

#### Steps

* You have determined that a public interface should be changed, but you do not want to break all clients. Implement the new interface, but "deprecate" the old one. The deprecation mechanism should inform clients that the interface has changed, and that a newer interface is recommended instead.

* Evaluate to what extent the deprecated interface continues to be used, and whether it can be permanently retired. Consider removing it in a future release.

* Java supports deprecation as a language feature:
- Deprecate a feature by adding the tag `@deprecated` to its javadoc documentation. The tag is not only recognized by the javadoc documentation generator, but the compiler will also generate compile-time warnings if code using deprecated features is compiled with the `-deprecated` option.

* Other approaches are:

- Simply inform users in the documentation which interfaces are deprecated.

- Move or rename the deprecated interface or component. Clients can continue to use them, but must adapt and recompile to continue to use the deprecated form.

- Replace deprecated components by equivalent ones that generate run-time warnings or output warnings to a log file.

- Alternatively, consider configuring the programming environment or the deprecated components themselves to generate compile-time or link-time warnings.

## Tradeoffs

### Pros

* Clients do not have to immediately adapt to changes.

* There is time to change your mind.

### Cons

* Clients are free to ignore deprecation.

## Difficulties

* It may be hard to track down all the clients of a deprecated component.

* It can be hard to decide when to really retire a deprecated component.

* If you want to keep the interface but change the semantics, you may need to introduce a new component and deprecate the old one. This can be the case if certain methods should now return default values instead of throwing exceptions (or vice versa).
## Known Uses

Perdita Stevens and Rob Pooley identify Deprecation as a common practice for managing evolving APIs in complex systems [SP98].

## Rationale

Deprecation gives you a window of time to evaluate the impact of a change.
## 7.11 Conserve Familiarity

**Intent** *Avoid radical changes that may alienate users.*

### Problem

How do you accomplish a major overhaul of a legacy system without disrupting the way users are used to getting their job done?

*This problem is difficult because:*

* The legacy system requires significant changes.

* The users are not happy with the legacy system, but they understand it well.

*Yet, solving this problem is feasible because:*

* You can migrate incrementally to a new solution.

### Solution

Introduce only a constant, relatively low number of changes between each new release.

### Tradeoffs

#### Pros

* Users do not have to change their work habits too much between releases.

#### Difficulties

* Sometimes radical change is necessary. It can be hard to migrate from a command-line interface to a GUI while conserving familiarity.

### Rationale

Too much change between releases increases the risk of hidden defects, and decreases the chance of user acceptance.
Lehman and Belady’s “Law of Conservation of Familiarity” suggests that the incremental change between releases of a system stays roughly constant over time [LB85]. This is a relatively natural phenomenon because to do anything else introduces unnecessary risks.

## Related Patterns

To Conserve Familiarity you must Migrate Systems Incrementally. Involve the Users to understand what changes will be acceptable. Prototype the Target Solution to evaluate the potential impact of changes.
## 7.12 Use Profiler Before Optimizing

**Intent** Avoid squandering reengineering effort on needless "optimizations" by verifying where the bottlenecks are.

### Problem

When should you rewrite a clearly inefficient piece of code?

*This problem is difficult because:*

* When you are reengineering software, you are likely to encounter many naive algorithms in the legacy code.

* It can be hard to predict what will impact performance, and you can lose a lot of time on pure supposition.

* Optimized code is often more complex than simple, naive code.

*Yet, solving this problem is feasible because:*

* There are tools to tell you where you may have a performance problem.

### Solution

Whenever you are tempted to optimize a "clearly inefficient" part of the system, first use a profiler to determine whether it is actually a bottleneck.

Don't optimize anything unless your profiler tells you it will make a difference.

If you decide to go ahead, prepare benchmarks that will demonstrate the performance gains.

### Tradeoffs

#### Pros

* You do not waste time optimizing something that will not make a difference to overall performance.

#### Cons

* Naive algorithms will survive longer in the system.
## Rationale

The performance improvement that you can gain by optimizing a bit of code depends on how much time the program spends in that code in a typical run. A profiler will tell you how much time that is.

"Do it, then do it right, then do it fast" is a well-known aphorism that has been credited to many different sources. Very likely its origin is outside of the field of computer science. The rationale behind it is that you risk making a system complex and hard to maintain if you become preoccupied with performance issues too early. Instead, it is better to first find a solution that works, then clean it up once you understand it. Finally, if you can identify any important performance bottlenecks, that is the time to optimize just those parts that will make a difference.

As a corollary, it may even be a good idea to replace a bit of complex, "optimized" code by a simpler, "naive" solution, if that won't severely impact performance, but will make it easier to make other changes.

See also Davis' discussion of "Use Profiler Before Optimizing" [Dav95].

## Related Patterns

If you Refactor to Understand [p. 127], you will have started the second step to "do it right."
# Chapter 8

## Detecting Duplicated Code

Fowler and Beck have ranked duplicated code as the first of the top ten code smells indicating the need to refactor a piece of software [FBB+99]. As they like to explain it, whenever you duplicate a piece of code, you are taking out a loan, in the sense that you are getting something now (an almost ready-made piece of software) that you will have to pay for later. There is nothing wrong with taking out a loan, but you have a choice between paying back a small amount now (by taking out the time to refactor your code to eliminate the duplication) or paying back a lot later (in terms of increased complexity and maintenance costs).

Data from empirical studies show that typically between 8% and 12% of industrial software consists of duplicated code [DRD99]. Although this may not seem like much, in fact it is difficult to achieve very high rates of duplication. (Imagine what it would take to have a duplication rate of even 50%!) Duplication rates of 15 to 20% are therefore considered to be severe.

It is important to identify duplicated code for the following reasons:

* Duplicated code hampers the introduction of changes, since every implemented variant of a piece of functionality will have to be changed. Since it is easy to miss some variants, bugs are likely to pop up in other places.

* Duplicated code replicates and scatters the logic of a system instead of grouping it into identifiable artifacts (classes, methods, packages). It leads to systems that are more difficult to understand and to change. Instead of just having to understand relationship between logical parts you will have first to identify them and then understand their relationships.
Duplicated code arises for a variety of reasons:

* Whenever a programmer is implementing a piece of functionality that is remotely similar to something that has been done before, it is natural to use the existing code as a model for the new task. If it is a matter of recombining existing procedures, the task will be simple. But if the behavior is more complex, the easiest thing to do is to copy, paste and modify the old code to achieve the functionality. If both the old and new pieces of code belong to different applications, the harm is minimal. But if they are part of the same system, duplicated code has now been introduced.

* Sometimes code is copied, pasted and modified between different applications, or different versions of the same application. When multiple versions must be maintained simultaneously, or when different applications or versions must be merged, you immediately have a duplicated code problem.

From a reengineering perspective, usually people know whether or not a system suffers from duplication. First, the development team, or the manager will tell you. Second, there are normally some clear signs that duplication has been practiced in a project: for example, two developers cannot develop four millions of line of code in less than eight months without copying and pasting existing code. While analyzing the system you will also identify duplicated code by accident. There is a major difference, however, between knowing that a system contains duplicated code, and knowing exactly which parts have been duplicated.

<figure><img src="image_35.png" /><figcaption>Figure 8.1: Two patterns to support Detecting Duplicated Code.</figcaption></figure>
Overview

Detecting Duplicated Code consists of two patterns: Compare Code Mechanically, which describes how we can detect duplicated code, and Visualize Code as Dotplots, which shows how duplicated code can be better understood by simple matrix visualization.

Once you have detected and understood duplication in the system, you may decide on a variety of tactics. Various refactoring patterns, such as Extract Method [p. 317] may help you to eliminate the duplication. Duplication may be a sign of misplaced responsibilities, in which you should may decide to Move Behavior Close to Data [p. 243].

Complex conditional statements are also a form of duplication, and may indicate that multiple clients have to duplicate actions that should belong to the target class. The pattern cluster Transform Conditionals to Polymorphism can help you to resolve these problems.
## 8.1 Compare Code Mechanically

**Intent** Discover duplicated code by comparing all the source code files line-by-line.

### Problem

How do you discover which parts of an application code have been duplicated?

*This problem is difficult because:*

* You may suspect that code has been duplicated but you do not have any a priori evidence where the duplication occurs. For example, you know that two programmers cannot have developed 4 million lines of Cobol in one year without having duplicated some code.

* Browsing the code is not an effective way of discovering duplication; you will only find duplicated code by accident.

* Programmers may have not only copied and pasted code but also modified variables or changed the shape of the programs.

*Yet, solving this problem is feasible because:*

* Most duplicated code can be detected by mechanical procedures.

### Solution

Textually compare each line of the software source code with all the other lines of code.

#### Steps

* Normalize the lines of code by removing comments, tabs and blanks.

* Remove lines that contain uninteresting code elements (e.g., just `else` or `})`

* Compare each line with all the other lines. Reduce search complexity by hashing:

- Preprocessing: Compute the hash value for each line

- Actual Comparison: Compare all lines in the same hash bucket
## Variants

This approach may fail to identify some instances of duplicated code due to renaming of variables. By deleting all variable identifiers, or by mapping them to a common symbol, you can detect similar code patterns, while abstracting from the details of the specific identifiers. This variant, however, requires some syntactic processing of the code.

## Tradeoffs

### Pros

* The approach is simple and gives good results while only requiring modest resources.

* It is nearly language-independent in the sense that you only have to build a lexical analyzer and not a full parser. That's why a simple perl script can be sufficient depending on the level of sophistication that you want.

* Simple statistics and percentage rates are easily computed and may help you to gain credibility or more strength in discussions on re-source allocation or hiring new people.

### Cons

* Code that has been heavily edited after copying may be hard to identify as duplicated code.

* Systems containing a lot of duplicated code will generate a lot of data that can be difficult to analyze effectively.

## Example

Consider the case of a system written in C++ where you suspect duplicated code. However, you didn't write to code yourself so you don't know where the actual duplication occurs. How can you detect where the duplicated code fragments are? Consistent with Keep It Simple [p. 37] you do the simplest thing that may possibly work: you write a little script that first normalizes the code to remove all white space from the code and afterwards compares each line of code against itself.

The normalization would change the following code
... // assign same fastid as container fastid = NULL; const char* fidptr = getFastid(); if(fidptr != NULL) { int l = strlen(fidptr); fastid = new char[l+1]; char *tmp = (char*) fastid; for (int i =0;i<l;i++) tmp[i] = fidptr[i]; tmp[l] = '0'; } ...

into

... fastid=NULL; constchar*fidptr=getFastid(); if(fidptr!=NULL) intl=strlen(fidptr); fastid=newchar[l+1]; char*tmp=(char*)fastid; for(inti=0;i<l;i++) tmp[i]=fidptr[i]; tmp[l]='0'; ...

Afterwards, the line-by-line comparison of the code against itself pro- duces a report telling which sequences of lines are duplicated.

Lines:fastid=NULL;;constchar*fidptr=getFastid();;if(fidptr!=NULL); intl=strlen(fidptr);;fastid=newchar[l+1];; Locations: </typesystem/Parser.C>6178/6179/6180/6181/6182 </typesystem/Parser.C>6198/6199/6200/6201/6202

Below is a sample of a perl script that will do the trick.

#!/usr/bin/env perl -w # duplocForCPP.pl - detect duplicated lines of code (algorithm only) # Synopsis: duplocForCPP.pl filename ... # Takes code (or other) files and collects all line numbers of lines # equal to each other within these files. The algorithm is linear (in # space and time) to the number of lines in input.

# Output: Lists of numbers of equal lines.
# Author: Matthias Rieger

$equivalenceClassMinimalSize = 1;
$slidingWindowSize = 5;
$removeKeywords = 0;

@keywords = qw(if
then
else
for
{
}
);

$keywordsRegExp = join ' ', @keywords;

@unwantedLines = qw( else
return
return;
return result;
)else{
#else
#endif
{
}
:
};
);

push @unwantedLines, @keywords;

@unwantedLines{@unwantedLines} = (1) x @unwantedLines;

$totalLines = 0;
$emptyLines = 0;
$codeLines = 0;
@currentLines = ();
@currentLineNos = ();
%eqLines = ();
$inComment = 0;

$start = (times)[0];

while (<>) {
chomp;
$totalLines++;

# remove comments of type /* */
```perl
my $codeOnly = "";
while (($inComment && m|\*/|) || ($!inComment && m|\*|)) {
    unless ($inComment) { $codeOnly .= $` }
    $inComment = $!inComment;
    $_ = $';
}
$codeOnly .= $_ unless $inComment;
$_ = $codeOnly;

s|//.*$|; # remove comments of type //
s/\s+//g; #remove white space
s/$keywordsRegExp//og if $removeKeywords; #remove keywords

# remove empty and unwanted lines
if (($!_ && $emptyLines++)
    || (defined $unwantedLines{$_} && $codeLines++)) { next }

$codeLines++;
push @currentLines, $_;
push @currentLineNos, $;
if ($slidingWindowSize < @currentLines) {
    shift @currentLines;
    shift @currentLineNos;
}

# print STDERR "Line $totalLines >$_<\n";

my $lineToBeCompared = join "", @currentLines;
my $lineNumbersCompared = "<$ARGV>"; # append the name of the
file
$lineNumbersCompared .= join '/', @currentLineNos;
# print STDERR "$lineNumbersCompared\n";
if ($bucketRef = $eqLines{$lineToBeCompared}) {
    push @$bucketRef, $lineNumbersCompared;
} else {
    $eqLines{$lineToBeCompared} = [ $lineNumbersCompared ];
}

if (eof) { close ARGV } # Reset linenumber-count for next file
}

$end = (times)[0];
$processingTime = $end - $start;

# print the equivalence classes
$numOfMarkedEquivClasses = 0;
```
```perl
$numOfMarkedElements = 0;
foreach $line (sort { length $a <=> length $b } keys %eqLines) {
    if(scalar @{$eqLines{$line}} > $equivalenceClassMinimalSize) {
        $numOfMarkedEquivClasses++;
        $numOfMarkedElements += scalar @{$eqLines{$line}};
        print "Lines: $line\n";
        print "Locations: @{$eqLines{$line}}\n\n";
    }
}

print "\n\n\n";
print "Number of Lines processed: $totalLines\n";
print "Number of Empty Lines: $emptyLines\n";
print "Number of Code Lines: $codeLines\n";
print "Scanning time in seconds: $processingTime\n";
print "Lines per second: @{$[ $totalLines/$processingTime] }\n";
print "-----------------------------------------\n";
print "Total Number of equivalence classes: @{$[scalar keys %eqLines]}\n";
print "Size of Sliding window: $slidingWindowSize\n";
print "Lower bound of eqiv-class Size: $equivalenceClassMinimalSize\n";
print "Number of marked equivalence classes:
    $numOfMarkedEquivClasses\n";
print "Number of marked elements: $numOfMarkedElements\n";
```

## Known Uses

In the context of software reengineering, the pattern has been applied to detect duplicated code in FAMOOS case studies containing up to one million lines of C++. It also has been applied to detect duplicated code in a COBOL system of 4 million lines of code. DATRIX has investigated multiple versions of a large telecommunications system, wading through 89 million lines of code all in all [LPM+97].
## 8.2 Visualize Code as Dotplots

**Intent** Gain insight into the nature of the duplication by studying the patterns in the dotplots.

### Problem

How can you gain insight into the scope and nature of code duplication in a software system?

*This problem is difficult because:*

* Just knowing where in the system duplicated code exists does not necessarily help you to understand its nature, or what should be done about it.

*Yet, solving this problem is feasible because:*

* A picture is worth a thousand words.

### Solution

Visualize the code as a matrix in which the two axes represent two source code files (possibly the same file), and dots in the matrix occur where source code lines are duplicated.

#### Steps

If you want to analyze two files A and B:

* Normalize the contents of the two files to eliminate noise (white space *etc.*).

* Let each axis of the matrix represent elements (*e.g.*, the lines of code) of the normalized files.

* Represent a match between two elements as a dot in the matrix.

* Interpret the obtained pictures: a diagonal represents duplicated code between the two files.

To analyze the duplication inside a single file, plot the elements of that file on both axes.
<figure><img src="image_36.png" /><figcaption>Figure 8.2: Possible sequences of dot and their associated interpretations.</figcaption></figure>

## Interpretations

The interpretation of the obtained matrices are illustrated in Figure 8.2:

Some interesting configurations formed by the dots in the matrices are the following:

* *Exact Copies:* diagonals of dots indicate copied sequences of source code.

* *Copies With Variations:* sequences that have holes in them indicate that a portion of a copied sequences has been changed.

* *Inserts/Deletes:* broken sequences with parts shifted to the right or left indicate that a portion of code has been inserted or deleted.

* *Repetitive Code Elements:* rectangular configurations indicate periodic occurrences of the same code. An example is the break at the end of the individual cases of a C or C++ switch statement, or recurring preprocessor commands like `#ifdef SOME CONDITION`.

## Tradeoffs

### Pros

* The approach is largely language-independent, since only the code normalization depends on the language syntax.

* The approach works well when reverse engineering large amounts of unknown code, because the dotplots attract your eye to certain parts of the code to be studied more closely.

* The idea is simple yet works surprisingly well. A simple version of the approach can be implemented by a good programmer using a
<figure><img src="image_37.png" /><figcaption>Figure 8.3: Code duplication before and after refactoring.</figcaption></figure>

appropriate tools in a couple of days. (One of our better students made a small dotplot browser in Delphi in two days.)

### Cons

* Dotplots only present pairwise comparisons. They do not necessarily help you identify all instances of duplicated elements in the entire software system. Although the approach can easily be extended to present multiple files across each axis, the comparisons are still only pairwise.

### Difficulties

* A naive implementation of a dotplot visualizer may not scale well to large systems. Tuning and optimizing the approach for large data sets can compromise the simplicity of the approach.

* The interpretation of the data may be more subtle than it appears at first glance. Indeed, while comparing multiple files the diagonals
represent more duplication than is really in the system because we
are comparing duplicated fragments with themselves over different
files, as shown by Figure 8.3 and Figure 8.4.

* The screen size limits the amount of information that can be visualized. Some success has been achieved with so-called "mural" visualization approaches [JS96]. However, these techniques are significantly more difficult to implement than simple dotplots and are not worth the extra effort.

Example

In Figure 8.3 we see a dotplot of two versions of a piece of software, before
and after the duplication has been removed. The first version is compared
to itself in the top left square. The line down the diagonal simply shows us
that every line of code is being compared to itself. What is more interesting
is that several other diagonal lines occur in the dotplot, which means that
code has been duplicated within this file. A second version of the same file
is compared to itself in the lower right square. Here we see no significant
duplication aside from the main diagonal, which reflects the fact that all
the duplicated code has been successfully refactored.

<figure><img src="image_38.png" /><figcaption>Figure 8.4: A Python file A being compared to itself and to a second file B.</figcaption></figure>

The bottom left and top right squares are mirror images of each other. They tell us how the before and after files have been reorganized. Since there is no strong diagonal, this tells us that significant reorganization has taken place. The diagonal stripes show us which parts of the old version have survived and where they appear in the new version. From the dot-
<figure><img src="image_39.png" /><figcaption>Figure 8.5: Dotplots produced by four switch statements.</figcaption></figure>

plot alone, we can guess that about half of the code has survived, and another half of the code has been significantly rewritten.

Dotplots are also useful to detect duplication across multiple files. Figure 8.4 shows a dotplot comparing two Python files. The comparison of A vs. A shows that there is essentially no internal duplication. Very likely there are some switch statements in the bottom have of the file, indicated by the matrix pattern.

When we compare file A to file B, however, we detect a staggering amount of duplication. It looks very much like file B is just a copy of file A that has been extended in various ways. Closer investigation showed this to be the case. In fact, file A was just an older version of file B that had inadvertently been left in the release.

Dotplots can also be useful to detect other problems. Figure 8.5 presents four clones that represent a switch statement over a type variable that is used to call individual construction code. The duplicated code could perhaps be eliminated by applying Transform Conditionals to Polymorphism.
## Known Uses

The pattern has been applied in biological research to detect DNA sequences [PK82]. The Dotplot tool [Hel95] has been used to detect similarities in manual pages, literary texts and names from file systems. In the FAMOOS project, the pattern has been applied to build Duploc, a tool for identifying duplication in software source code [DRD99]. The Dup tool [Bak92] has been used to investigate the source code of the X-Windows system and uses a dotplot matrix graphical representation.

## Related Patterns

Once you have detected duplicated code, numerous refactoring patterns may apply, in particular Extract Method [p. 317].

Very often duplicated code arises because clients assume too many responsibilities. In that case, Move Behavior Close to Data [p. 243] will help you to eliminate the duplication.

Dotplots also help to detect large conditional constructs. You should probably Transform Conditionals to Polymorphism to eliminate these conditionals and thereby achieve a more flexible design.
Chapter 9

Redistribute
Responsibilities

You are responsible for reengineering the information system that man-
ages all employee records for a large public administration. Due to recent
political upheavals, you know that there will be many changes required in
the system to cope with privatization, new laws, and new regulations, but
you do not know exactly what they will be. The existing system consists of
a nominally object-oriented reimplementation of an older procedural sys-
tem. The code contains many pseudo-objects: data containers masquerad-
ing as objects, and big, procedural "god classes" that implement most of
a the logic of individual subsystems. One class, called TaxRevision2000,
has a single method consisting essentially of a case statement that is 3000
lines long.

As long as the system was relatively stable, this design posed no par-
ticular problems, but now you see that even relatively modest changes to
system require months of planning, testing and debugging due to weak
encapsulation of data. You are convinced that migrating to a more object-
oriented design will make the system more robust and easier to adapt
to future requirements. But how do you know where the problems lie?
Which responsibilities should be redistributed? Which data containers
should you redesign, which ones should you wrap, and which ones are
better left alone?
## Forces

* Data containers (objects that just provide access to data, but no own behavior) are a simple and convenient way to share information between many subsystems. Among others, data containers are the easiest way to provide access to database entities.

* However, data containers expose the data representation, hence are difficult to change when many application components depend on them. Consequently, a proliferation of data containers leads to fragile navigation code in the implementation of business logic.

* It is hard to teach an old dog new tricks. Many designers received a training in functional decomposition and will use the same habits when doing an object design.

* However, functional decomposition tends to generate god classes, i.e., big classes that do all of the work and have a myriad of tiny provider classes around of it. God classes are hard to extend, modify or subclass because such changes affect large numbers of other methods or instance variables.

## Overview

This cluster deals with problems of misplaced responsibilities. The two extreme cases are *data containers*, classes that are nothing but glorified data structures and have almost no identifiable responsibilities, and *god classes*, procedural monsters that assume too many responsibilities.

Although there are sometimes borderlines cases where data containers and god classes may be tolerated, particularly if they are buried in a stable part of the system which will not change, generally they are a sign of a fragile design.

Data containers lead to violations of the *Law of Demeter* (LOD) [LHR88]. In a nutshell, the Law of Demeter provides a number of design guidelines to reduce coupling between distantly-related classes. Although the Law of Demeter has various forms, depending on whether one focusses on objects or classes, and depending on which programming language is being used, the law essentially states that methods should only send messages to instance variables, method arguments, self, super, and the receiver class.

Violations of the Law of Demeter typically take the form of *navigation code* in which an *indirect client* accesses an *indirect provider* by accessing either an instance variable or an acquaintance of an *intermediate provider*. The indirect client and provider are thereby unnecessarily coupled, making future enhancements more difficult to realize (Figure 9.2). The intermediate
provider may take the form of a data container or opens its encapsulation by providing accessor methods. Designs with many data containers present often suffer from complex navigation code in which indirect clients may have to navigate through a chain of intermediates to reach the indirect provider.

<figure><img src="image_40.png" /><figcaption>Figure 9.1: An indirect client violates the Law of Demeter by navigating through an intermediate provider to an indirect provider, unnecessarily coupling the two.</figcaption></figure>

Whereas data containers have too few responsibilities, god classes assume too many. A god class can be a single class that implements an entire subsystem, consisting of thousands of lines of code and hundreds of methods and instance variables. Particularly vicious god classes consist of only static instance variables and methods, i.e., all data and behavior have class scope, and the god class is never instantiated. Such god classes are purely procedural beasts, and are object-oriented in name only.

Occasionally some procedural classes known as *utility classes* are convenient. The best known examples are object-oriented interfaces to math libraries, or collections of algorithms. Real god classes, however, are not libraries, but complete applications or subsystems that controls the entire application execution.

God classes and data containers often occur together, with the god class assuming all the control of the application, and treating other classes as glorified data structures. Since they assume too many responsibilities, god classes are hard to understand and maintain. Incremental modification and extension of a god class through inheritance is next to impossible due to the complexity of its interface and the absence of clear subclassing contract.

This cluster provides a number of patterns to eliminate data containers and god classes by redistributing responsibilities and thereby improving encapsulation.

* Move Behavior Close to Data [p. 243] moves behavior defined in
<figure><img src="image_41.png" /><figcaption>Figure 9.2: Data containers are the clearest sign of misplaced responsibilities. These three patterns redistribute responsibilities by moving behavior close to data.</figcaption></figure>

indirect clients to an intermediate data container to make it more “object-like”. This pattern not only decouples indirect clients from the contents of the data container, but also typically eliminates duplicated code occurring in multiple clients of the data container.

* Eliminate Navigation Code [p. 253] is technically very similar to Move Behavior Close to Data in terms of the reengineering steps, but is rather different in its intent. This pattern focusses on redistributing responsibilities down chains of data containers to eliminate navigation code.

* Split Up God Class [p. 263] refactors a procedural god class into a number of simple, more cohesive classes by moving all data to external data containers, applying Move Behavior Close to Data to promote the data containers to objects, and finally removing or deprecating the facade that remains.
## 9.1 Move Behavior Close to Data

**Intent** Strengthen encapsulation by moving behavior from indirect clients to the class containing the data it operates on.

### Problem

How do you transform a class from being a mere data container into a real service provider?

*This problem is difficult because:*

* Data containers offer only accessor methods or public instance variables, and not real behavior, forcing clients to define the behavior themselves instead of just using it. New clients typically have to reimplement this behavior.

* If the internal representation of a data container changes, many clients have to be updated.

* Data containers cannot be used polymorphically since they define no behavior and their interfaces consist mainly of accessor methods. As a consequence, clients will be responsible for deciding which behavior is called for in any given context.

*Yet, solving this problem is feasible because:*

* You know what operations clients perform with the data.

### Solution

Move behavior defined by indirect clients to the container of the data on which it operates.

### Detection

Look for:

* Data containers, i.e., classes defining mostly public accessor methods and few behavior methods (i.e., the number of methods is approximately 2 times larger than the number of attributes).
<figure><img src="image_42.png" /><figcaption>Figure 9.3: Classes that were mere data containers are transformed into real service providers.</figcaption></figure>

* Duplicated client code that manipulates data of separate provider classes. If multiple clients implement different behavior, consider instead applying Transform Client Type Checks [p. 281].

* Methods in client classes that invoke a sequence of accessor methods (see Eliminate Navigation Code).

## Steps

Move Behavior Close to Data makes use of the refactorings Extract Method [p. 317] and Move Method [p. 317], since the behavior in question will have to be extracted from a client method and then moved to a provider class.

1. Identify the client behavior that you want to move, i.e., the complete method or a part of a method that accesses provider data.

* Look for the invocations of the accessor methods of the data container.

* Look for duplicated code in multiple clients that access the same provider data.

2. Create the corresponding method in the provider class, if it does not already exist. Be sure to check that moving the code will not introduce
any naming conflicts. Tools like the Refactoring Browser [RBJ97] au-
tomate these steps:

* If the extracted functionality is a complete method with arguments, check that the arguments do not conflict with attributes of the provider class. If so, rename the arguments.

* If the extracted functionality uses temporary variables, check that the local variables do not conflict with attributes or variables in the target scope. If so, rename the temporary variables.

* Check if the extracted functionality accesses local variables of the client classes (attributes, temporary variables,...), if so, add arguments to the method to represent these client variables.

3. *Give an intention-revealing name to the new method.* Among others, intention revealing names do not contain references to the class they belong to, because this makes the method less reusable. For instance, instead of defining a method `intoSet()` on a class `Set`, it is better to name it simply `add()`. Similarly, it is not such a good idea to define a method `binarySearch()` on a class `Array`, because the method name implies a sorted random access collection, while the name `search()` does not have such implications.

4. In the client *invoke the new provider method* with the correct parameters.

5. *Clean up the client code.* In the case the moved functionality was a complete method of the client class:

* check all the methods that invoke the old, moved method and ensure that they now call the new provider method instead, and

* remove the old method from the client or deprecate it. (Deprecate Obsolete Interfaces [p. 215]).

It may be the case that the calling methods defined on the same object
have to be also moved to the provider. In such a case repeat the steps
for the methods.

6. *Repeat* for multiple clients. Note that duplicated code in multiple clients will be removed in step 2, since there is no need to move code that has already been transferred to the provider. In case many similar, but not identical methods are introduced to the provider, consider factoring out the duplicated fragments as protected helper methods.
# Tradeoffs

## Pros

* Data containers are converted to service providers with clear responsibilities.

* The service providers become more useful to other clients.

* Clients are no longer responsible for implementing provider behavior.

* Clients are less sensitive to internal changes of the provider.

* Code duplication in the system decreases.

## Cons

* If the moved behavior also accesses client data, turning these accesses into parameters will make the interface of the provider more complex and introduce explicit dependencies from the provider to the client.

## Difficulties

* It may not be clear whether client code really should be moved to the data provider. Some classes like `Stream` or `Set` are really designed as data providers. Consider moving the code to the provider if:

- the functionality represents a *responsibility* of the provider. For example, a class `Set` should provide mathematical operations like union and intersection. On the other hand, a generic `Set` should not be responsible for operations on sets of `Employees`.

- the functionality accesses the attributes of the provider,

- the functionality is defined by multiple clients.

* If the provider is really designed as a data container, consider defining a new provider class that wraps an instance of the data provider and holds the associated behavior. For example, an `EmployeeSet` might wrap a `Set` instance and provide a more suitable interface.

## When the legacy solution is the solution

Data containers may have been automatically generated from a database schema to provide an object interface to an existing database. It is almost
always a bad idea to modify generated classes, since you will lose your changes if the code ever needs to be regenerated. In this case, you may decide to implement wrapper classes to hold the behavior that should be associated with the generated classes. Such a wrapper would function as an Adapter [p. 319] that converts the generated data container to a real service provider.

Sometimes you know that a class defined in a library is missing crucial functionality. For example, an operation *convertToCapitals* that is missing for class *String*. In such a case it is typically impossible to add code to the library, so you may have to define it in client class. In C++ for example, it may be the only way to avoid recompilation or to extend a class when the code is not available [ABW98] (p. 378). In Smalltalk you have the possibility to extend or modify the library, however you should pay particular attention to separate the additional code so you can easily merge it with future releases of the library, and quickly detect any conflicts.

The intent of the Visitor [p. 322] design pattern states: *"Represent an operation to be performed on the elements of an object structure in a class separate from the elements themselves. Visitor lets you define a new operation without changing the classes of the elements on which it operates"* [GHJV95]. The Visitor pattern is one of the few cases where you want to have classes access the data of a separate provider class. Visitor allows one to dynamically add new operations to a set of stable classes without having to change them.

*Configuration classes* are classes that represent the configuration of a system (e.g., global parameters, language dependent representation, policies in place). For example, in a graphic tool the default size of the boxes, edges, width of the lines can be stored in a such class and other classes refer to it when needed.

*Mapping classes* are classes used to represent mappings between objects and their user interface or database representation. For example, a software metric tool should graphically represent the available metrics in a widget-list so that the user can select the metrics to be computed. In such a case the graphical representation of the different metrics will certainly differ from their internal representation. A mapping class keeps track of the association.

### Example

One of the recurring complaints of the customers is that it takes too much time to change the reports generated by the information system. By talking to the maintainers you learn that they find generating the reports quite boring. "Its's always the same code you have to write," says Chris, one
<figure><img src="image_43.png" /><figcaption>Figure 9.4: The `Payroll` and `Telephone` classes access the internal representation of the class `Employee` to print a representation.</figcaption></figure>

of the maintainers. "You fetch a record out of the database, print its fields and then proceed to the next record."

You strongly suspect a case of data-containers and a closer examination of the code confirms your suspicion. Almost all of the classes interfacing with the database contain accessor methods only, and the programs generating reports are forced to use these accessors. One striking example is the case of the `Payroll` application, which has lots in common with the `TelephoneGuide` application and you decide to try to move the common functionality to the `Employee` class.

Before

As shown in Figure 9.4, both the `Payroll` and `TelephoneGuide` classes print labels, treating `Employee` instances as data containers. Thus, `Payroll` and `TelephoneGuide` are indirect clients of the attributes of `Employee`, and define printing code that should have been provided by the `Employee` class. The following code show how this would look like in Java.

```java
public class Employee {
    public String[] telephoneNumbers = {};
    ...
    public String name() {
        return name;
    }
    public String address() {
        return address;
    }
}

public class Payroll {
```
```java
public static Employee currentEmployee;

public static void printEmployeeLabel () {
    System.out.println(currentEmployee.name());
    System.out.println(currentEmployee.address());
    for (int i=0; i < currentEmployee.telephoneNumbers.length; i++) {
        System.out.print(currentEmployee.telephoneNumbers[i]);
        System.out.print(" ");
    }
    System.out.println("");
}

...
}
```

```java
public class TelephoneGuide {
    public static void printEmployeeTelephones (Employee emp) {
        System.out.println.emp.name());
        System.out.println.emp.address());
        for (int i=0; i < emp.telephoneNumbers.length - 1; i++) {
            System.out.print emp.telephoneNumbers[i]);
            System.out.print(" -- ");
        }
        System.out.print emp.telephoneNumbers[i].length - 1);
        System.out.println("");
    }
    ...
}
```

Note that although both print methods implement essentially the same functionality, there are some slight differences. Among others, TelephoneGuide.printEmployeeTelephones uses a different separator while printing out the telephone numbers.

## Steps

The different separators can easily be dealt with by defining a special parameter representing the separator to be used. Thus TelephoneGuide.printEmployeeTelephones gets rewritten as follows.

```java
public static void printEmployeeTelephones
    (Employee emp, String separator) {
    ...
    for (int i=0; ...
         System.out.printosa);
    ...}
    ...
}
```
Next, move the `printEmployeeTelephones` method from `TelephoneGuide` to `Employee`. Thus, copy the code and replace all references to the `emp` parameter with a direct reference to the attributes and methods. Also, ensure that the new method has an intention revealing name, thus omit the `Employee` part from the method name, resulting in a method `printLabel`.

```java
public class Employee {
    ...
    public void printLabel(String separator) {
        System.out.println(name);
        System.out.println(address);
        for (int i=0; i < telephoneNumbers.length - 1; i++) {
            System.out.print(telephoneNumbers[i]);
            System.out.print sepener;
        }
        System.out.print(telephoneNumbers[telephoneNumbers.length - 1]);
        System.out.println("");
    }
}
```

Then replace the method bodies of `Payroll.printEmployeeLabel` and `TelephoneGuide.printEmployeeTelephones` with a simple invocation of the `Employee.printLabel` method.

```java
public class Payroll {
    ...
    public static void printEmployeeLabel () {
        currentEmployee.printLabel(" ");
    }
    ...
}

public class TelephoneGuide {
    ...
    public static void printEmployeeTelephones (Employee emp) {
        emp.printLabel(" -- ");
    }
    ...
}
```

Finally, verify which other methods refer to the `name()`, `address()` and `telephoneNumbers`. If no such methods exist, consider to declare those methods and attributes as private.

## After

After applying `Move Behavior Close to Data` the class `Employee` now provides a `printLabel` method which takes one argument to represent the different separators (see Figure 9.5). This is a better situation because now
<figure><img src="image_44.png" /><figcaption>Figure 9.5: The Payroll class uses the public interface of the class Employee to print a representation of Employee; data accessors became private.</figcaption></figure>

clients do not rely on the internal representation of *Employee*. Moreover, by moving the behavior near the data it operates, the class represents a conceptual entity with an emphasis on the services it provides instead of structure it implements.

## Rationale

> Keep related data and behavior in one place.

> — Arthur Riel, Heuristic 2.9 [Rie96]

Data containers impede evolution because they expose structure and force clients to define their behavior rather than sharing it. By promoting data containers to service providers, you reduce coupling between classes and improve cohesion of data and behavior.

## Related Patterns

Encapsulate Field [p. 317] offers heuristics that help determine where methods should be defined during a design phase. The text offers ratio-nale for applying Move Behavior Close to Data.
## 9.2 Eliminate Navigation Code

*Also Known As: Law of Demeter [LHR88]*

**Intent** Reduce the impact of changes by shifting responsibility down a chain of connected classes.

### Problem

How do you reduce coupling due to classes that navigate through the object graph?

*This problem is difficult because:*

* Changes in the interfaces of a class will affect not only direct clients, but also all the indirect clients that navigate to reach it.

*Yet, solving this problem is feasible because:*

* Navigation code is typically a sign of misplaced responsibilities and violation of encapsulation.

### Solution

Iteratively move behavior defined by an indirect client to the container of the data on which it operates.

Note that actual reengineering steps are basically the same as those of Move Behavior Close to Data, but the manifestation of the problem is rather different, so different detection steps apply.

### Detection

Look for indirect providers:

* Each time a class changes, e.g., by modifying its internal representation or collaborators, not only its direct but also indirect client classes have to be changed.

* Look for classes that contain a lot of public attributes, accessor methods or methods returning as value attributes of the class.
* Big aggregation hierarchies containing mostly data classes often play the role of indirect provider.

Look for *indirect clients* that contain a lot of *navigation code*. Navigation code is of two kinds:

* a sequence of attribute accesses, e.g., a.b.c.d where b is an attribute of a, c is an attribute of b and d an attribute of c. The result of such a sequence can be assigned to variable or a method of the last object can be invoked, e.g., a.b.c.d.op(). Such a sequence navigation does not occur in Smalltalk where all the attributes are protected.

* a sequence of accessor method calls. In Java and C++ such a sequence has the form `object.m1().m2().m3()` where `object` is an expression returning an object, `m1` is a method of `object`, `m2` a method of the object returned by the invocation of `m1`, `m3` a method of the object returned by the invocation of `m2` and so on. In Smalltalk navigation code has the following form receiver `m1 m2 ... mn`. The same navigation code sequence is repeated in different methods on the same or different clients.

Navigation code can be detected by simple pattern matching. However, to really detect a method call navigation sequence leading to coupled classes, you should filter out sequences of calls converting one object to another one. For example, the following two Java expressions are not problematic because they deal with object conversion.

```
leftSide().toString()
i.getValue().isShort()
```

To deal with this case you can:

* look for more than two calls, or

* eliminate from consideration known object conversion calls, including standard method invocations for converting to and from primitive types.

The use of additional variables, can sometimes disguise navigation code, so reading the code is often necessary. For instance, the following Java code does not contain a chain of invocations.

```java
Token token;
token = parseTree token();
if (token identifier() != null) {
    ...
}
```
However, it is equivalent to the following code, which does contain a chain of invocations

```javascript
if (parseTree token().identifier() != null) {
    ...
```

*Smalltalk.* Simply searching for sequences of calls in Smalltalk code can create a lot of noise because Smalltalk does not have predefined control structures but uses messages even for implementing control structures. The above example with the disguised navigation code would read as follows in Smalltalk. (Note the messages isNil and ifFalse:[...])

```
| token |
token := parseTree token.
token identifier isNil ifFalse:[...]
```

The equivalent version with navigation code becomes.

```javascript
parseTree token identifier isNil ifFalse: [...]
```

The following code segments contain a sequence of invocations but do not pose any problems because the first deals with boolean testing and the second with conversion (abuse of conversion, in fact).

```
(a isNode) & (a isAbstract) ifTrue: [...]
aCol asSet asSortedCollection asOrderedCollection
```

*Java.* For Java or C++, primitives data types and control structures are not implemented using objects, so simple pattern matching produces less noise. For example, a simple Unix command like:

```
egrep '.*\(\).*\(\).*\(\)' *.java
egrep '.*\..*\..\.*\..' *.java
```

identifies lines of code like the following ones, which are examples of navigation code coupling between classes, and filters out the conversions mentioned above.

```javascript
a.getAbstraction().getIdentifier().traverse(this)
a.abstraction identifier traverse(this)
```

More sophisticated matching expressions can reduce the noise produced by the parentheses of casts or other combinations.

*AST Matching.* If you have a way to express tree matching, you can detect navigation code. For example, the Rewrite Rule Editor that comes with the Refactoring Browser [RBJ97] can detect navigation code using the pattern
<figure><img src="image_45.png" /><figcaption>Figure 9.6: Chains of data containers can be converted into service providers, thereby eliminating navigation code and reducing coupling between classes.</figcaption></figure>

`'@object 'mess1 'mess2 'mess3` To narrow the analysis of the results you should only consider messages that belong to the domain objects and eliminate all the method selectors of libraries objects like (`isNil`, `not`, `class`, ...).

## Steps

The recipe for eliminating navigation code is to recursively `Move Behavior Close to Data`. Figure 9.6 illustrates the transformation.

1. *Identify* the navigation code to move.

2. *Apply* `Move Behavior Close to Data` to remove one level of navigation. (At this point your regression tests should run.)

3. *Repeat*, if necessary.

*Caution.* It is important to note that the refactoring process relies on pushing code from the clients to the providers. In the example, from `Car` to `Engine` and from `Engine` to `Carburetor`. A common mistake is to try to eliminate
navigation code by defining accessors at the client class level that access the attributes of the provider attribute values, e.g., defining an accessor `getCarburetor` in the class `Car`. Instead of reducing coupling between the classes, it just increases the number of public accessors and makes the system more complex.

## Tradeoffs

### Pros

* Chains of dependencies between classes are eliminated, so changes in classes at the lowest level will impact fewer clients.

* Functionality that was implicit in the system is now named and explicitly available to new clients.

### Cons

* The systematic application of `Eliminate Navigation Code` may lead to large interfaces. In particular, if a class defines many instance variables that are collections, then `Eliminate Navigation Code` would force you to define a large number of additional methods to shield the underlying collections.

## Difficulties

* Deciding when to apply `Eliminate Navigation Code` can be difficult. Defining methods that merely delegate requests to class collaborators may not always be the solution. It may happen that giving away internal information can reduce the interface of a class. For example, if a class implements some well-defined behaviors but also serves as a Facade [p. 319] to other collaborators, it may be simpler to give access to the collaborator directly to reduce the interface of the class.

## When the legacy solution is the solution

Navigation code may be the best solution when objects are graphically presented or mapped to a database. In such cases the goal is to really expose and mimic the structural relationships between classes. Eliminating navigation code will be a futile exercise.
<figure><img src="image_46.png" /><figcaption>Figure 9.7: How to remove the unnecessary dependencies between the Reports class and the File and Employee Classes.</figcaption></figure>

It is sometimes necessary for a client to talk with its indirect providers. This is true when direct providers play the role of an object server that returns certain objects given certain properties (OOID, keys...). In this situation the client calls the object server (a direct provider) that returns objects (indirect providers) to which the client sends messages.

## Example

After having modified the Employee, Payroll and TelephoneGuide classes, you noticed that it took 1/2 an hour to rebuild the whole project. Next time you see Chris (one of the maintainers) you ask him why this build took so long. "You probably changed the Employee class" he answers, "we don't dare to touch that class anymore since so many classes depend on it".

You decide to examine this Employee class in further detail and find many unnecessary dependencies. For instance (as shown in Figure 9.7) there is a class Reports, implementing one method countHandledFiles,
which counts for each Department the number of files that are handled by all of its employees. Unfortunately, there is no direct relationship between Department and File and consequently the ReportHandledFiles must navigate over a department's employees to enumerate all the files and access the handled() status.

The Java code below shows the situation before and after applying Eliminate Navigation Code. The bold textual elements highlight problems and the solutions in the before and after situation.

### Before

```java
public class Reports {
    ...
    public static void countHandledFiles(Department department) {
        int nrHandled = 0, nrUnhandled = 0;

        for (int i=0; i < department.employees.length; i++) {
            for (int j=0; j < department.employees[i].files.length; j++) {
                if (department.employees[i].files[j].handled()) {
                    nrHandled++;
                }
                else {
                    nrUnhandled++;
                }
            }
        }
    }
}
```

The method countHandledFiles counts the number of handled files, by asking the current department its employees and for each of these files. The classes Department and Employee have to declare those attributes public. With this implementation, two problems occur:

1. The Reports class must know how to enumerate the associations between Department, Employee and File, and this information must be accessible in the public interface of each of the classes. If one of these public interfaces change, then this change will affect all associated classes.

2. The method countHandledFiles is implemented by directly accessing the variables employees and files. This unnecessarily couples the class Reports and the classes Department and Employee. If the class Department or Employee change the data-structure used to hold the associated objects, then all the methods in class Reports will have to be adapted.
Steps

The solution is to extract the nested for loops as separate methods and
move them on the appropriate classes. This is actually a two step process.

First extract the outer for loop from `Reports.countHandledFiles` as a
separate method (name it `countHandledFiles` as well) and move it to the
class `Department`.

```java
public class Department {
    ...
    public void countHandledFiles
        (Counter nrHandled, Counter nrUnhandled) {
        for (int i=0; i < this.employees.length; i++) {
            for (int j=0; j < this.employees[i].files.length; j++) {
                if (this.employees[i].files[j].handled()) {
                    nrHandled.increment();
                }
                else {
                    nrUnhandled.increment();}}}
    ...
}

public class Reports {
    ...
    private static void countHandledFiles(Department department) {
        Counter nrHandled = new Counter(0), nrUnhandled = new Counter
            (0);
        department.countHandledFiles(nrHandled, nrUnhandled);
    }
    ...
}
```

Next, extract the inner for loop from `Department.countHandledFiles`
(also named `countHandledFiles`) and move it to the class `Employee`.

```java
public class Employee {
    ...
    public void countHandledFiles
        (Counter nrHandled, Counter nrUnhandled) {
        for (int j=0; j < this.files.length; j++) {
            if (this.files[j].handled()) {
                nrHandled.increment();
            }
            else {
                nrUnhandled.increment();}}
    ...
}

public class Department {
    ...
    public void countHandledFiles
        (Counter nrHandled, Counter nrUnhandled) {
        for (int i=0; i < this.employees.length; i++) {
```
```c
this.employees[i].countHandledFiles(nrHandled, nrUnhandled);}}
```

If all direct accesses to the *employees* and *files* variables are removed, these attributes can be declared private.

## Rationale

A method "M" of an object "O" should invoke only the methods of the following kinds of objects.

1. itself

2. its parameters

3. any object it creates/instantiates

4. its direct component objects

— Law of Demeter

Navigation code is a well-known symptom of misplaced behavior [LK94] [Sha97] [Rie96] that violates the Law of Demeter [LHR88]. It leads to unnecessary dependencies between classes and as a consequence changing the representation of a class requires *all* clients to be adapted.

## Related Patterns

Eliminate Navigation Code and Compare Code Mechanically [p. 227] re-inforce each other: Navigation code that is spread across different clients spreads duplicated code over the system. Compare Code Mechanically helps to detect this phenomenon. Eliminate Navigation Code brings the duplicated code together, where it is easier to refactor and eliminate.
## 9.3 Split Up God Class

*Also Known As:* The Blob [BMMM98], God Class [Rie96]

**Intent** Split up a class with too many responsibilities into a number of smaller, cohesive classes.

### Problem

How do you maintain a class that assumes too many responsibilities?

*This problem is difficult because:*

* By assuming too many responsibilities, a god class monopolizes control of an application. Evolution of the application is difficult because nearly every change touches this class, and affects multiple responsibilities.

* It is difficult to understand the different abstractions that are intermixed in a god class. Most of the data of the multiple abstractions are accessed from different places.

* Identifying where to change a feature without impacting the other functionality or other objects in the system is difficult. Moreover, changes in other objects are likely to impact the god class, thus hampering the evolution of the system.

* It is nearly impossible to change a part of the behavior of a god class in a black-box way.

*Yet, solving this problem is feasible because:*

* You don't have to fix the problem in one shot.

* You can use Semantic Wrapper to wrap it and present interfaces.

### Solution

Incrementally redistribute the responsibilities of the god class either to its collaborating classes or to new classes that are pulled out the god class. When there is nothing left of the god class but a facade, remove or deprecate the facade.
## Detection

A god class may be recognized in various ways:

* a single huge class treats many other classes as data structures.

* a "root" class or other huge class has a name containing words like "System", "Subsystem", "Manager", "Driver", or "Controller".

* changes to the system always result in changes to the same class.

* changes to the class are extremely difficult because you cannot identify which parts of the class they affect.

* reusing the class is nearly impossible because it covers too many design concerns.

* the class is a domain class holding the majority of attributes and methods of a system or subsystem. (Note that the threshold is not absolute because some UI frameworks produce big classes with lots of methods, and some database interface classes may need a lot of attributes).

* the class has an unrelated set of methods working on separated instance variables. The cohesiveness of the class is usually low.

* the class requires long compile times, even for small modifications.

* the class is difficult to test due to the many responsibilities it assumes.

* the class uses a lot of memory.

* people tell you: "This is the heart of the system".

* when you ask for the responsibility of a god class you get various, long and unclear answers.

* god classes are the nightmare of maintainers, so ask what classes are huge and difficult to maintain. Ask what is the class they would not like to work on. (Variant: Ask people to choose which class they want to work on. The one that everybody avoids may be a god class.)

## Steps

The solution relies on incrementally moving behavior away from the god class. During this process, data containers will become more object-like by acquiring the functionality that the god class was performing on their data. Some new classes will also be extracted from the god class.
<figure><img src="image_47.png" /><figcaption>Figure 9.8: A god class is refactored in two stages, first by redistributing responsibilities to data containers, or by spawning off new classes, until there is nothing left but a facade, and second by removing the facade.</figcaption></figure>

The following steps describe how this process ideally works. Note, however, that god classes can vary greatly in terms of their internal structure, so different techniques may be used to implement the transformation steps. Furthermore, it should be clear that a god class cannot be cured in one shot, so a safe way to proceed is to first transform a god class into a lightweight god class, then into a Facade [p. 319] that delegates behavior to its acquaintances. Finally, clients are redirected to the refactored data containers and the other new objects, and the Facade can be removed. The process is illustrated in figure 39.

The following steps are applied iteratively. Be sure to apply Regression Test After Every Change [p. 201]:

1. Identify cohesive subsets of instance variables of the god class, and convert them to external data containers. Change the initialization