Refactoring to Patterns suggests that using patterns to improve an existing design is better than using patterns early in a new design. This is true whether code is years old or minutes old. We improve designs with patterns by applying sequences of low-level design transformations, known as refactorings.
This book was written to help you:
- Understand how to combine refactoring and patterns
- Improve the design of existing code with pattern-directed refactorings
- Identify areas of code in need of pattern-directed refactorings
- Learn why using patterns to improve existing code is better than using patterns early in a new design
This book assumes you are familiar with design concepts like tight coupling and loose coupling as well as object-oriented concepts like inheritance, polymorphism, encapsulation, composition, interfaces, abstract and concrete classes, abstract and static methods, and so forth.
Once my design skills had improved, I found myself using patterns in a different way: I began refactoring to, towards, and away from patterns, instead of using them for up-front design or introducing them too early into my code. My new way of working with patterns emerged from my adoption of extreme programming (XP) design practices, which helped me avoid both over- and under-engineering.
When you make your code more flexible or sophisticated than it needs to be, you over-engineer it. Some programmers do this because they believe they know their system’s future requirements. They reason that it’s best to make a design more flexible or sophisticated today, so it can accommodate the needs of tomorrow. That sounds reasonable—if you happen to be psychic.
Over-engineered code affects productivity because when programmers inherit an over-engineered design, they must spend time learning the nuances of that design before they can comfortably extend or maintain it.
Perhaps the main reason programmers over-engineer is that they don’t want to get stuck with a bad design. A bad design can weave its way so deeply into code that improving it becomes an enormous challenge. I’ve been there, and that’s why up-front design with patterns appealed to me so much.
When I first began learning patterns, they represented a flexible, sophisticated, even elegant way to do object-oriented design that I very much wanted to master. After thoroughly studying numerous patterns and pattern languages, I used them to improve systems I had already built and to formulate designs for systems I was about to build. Because the results of these efforts were promising, I felt sure I was on the right path.
But over time, the power of patterns led me to lose sight of simpler ways to write code. After learning that there were two or three different ways to do a calculation, I’d immediately race towards implementing the Strategy pattern, when, in fact, a simple conditional expression would have been easier and faster to program—a perfectly sufficient solution.
Experiences like this made me aware that I needed to stop thinking so much about patterns and refocus on writing small, simple, understandable code. I was at a crossroads: I’d worked hard to learn patterns to become a better software designer, but now I needed to relax my reliance on them in order to become truly better.
Under-engineering is far more common than over-engineering. We under-engineer when we produce poorly designed software. This may occur for several reasons.
- We don’t have time, don’t make time, or aren’t given time to refactor
- We aren’t knowledgeable about good software design
- We’re expected to quickly add new features to existing systems
- We’re made to work on too many projects at once
Continuous under-engineering leads to the “fast, slow, slower” rhythm of software development, which goes something like this.
- You quickly deliver release 1.0 of a system with junky code
- You deliver release 2.0 of the system, and the junky code slows you down
- As you attempt to deliver future releases, you go slower and slower as the junky code multiplies, until people lose faith in the system, the programmers, and even the process that got everyone into this position
- Somewhere during or after release 4.0, you realize you can’t win. You begin exploring the option of a total rewrite
Test-driven development (TDD) and continuous refactoring enable the efficient evolution of working code by turning programming into a dialogue.
- Ask: You ask a question of a system by writing a test
- Respond: You respond to the question by writing code to pass the test
- Refine: You refine your response by consolidating ideas, weeding out inessentials, and clarifying ambiguities
- Repeat: You keep the dialogue going by asking the next question
However, when using the Design Patterns book during design, many programmers, myself included, have read the Intent section of a pattern to see whether the pattern could provide a good fit for a given situation. This method of choosing a pattern doesn’t work as well as a method that helps you match a design problem to the problems addressed by a pattern. Why? Because patterns exist to solve problems, and learning whether they really can help in a given situation involves understanding what problems they help solve.
The refactoring literature tends to focus more on specific design problems than the patterns literature does. If you study the first page of a refactoring, you’ll see the kind of problem the refactoring helps solve. The catalog of pattern-directed refactorings presented in this book, which is a direct continuation of work started in Refactoring, is intended to help you see what kinds of specific problems the patterns help solve.
There is a natural relation between patterns and refactorings. Patterns are where you want to be; refactorings are ways to get there from somewhere else.
If you’d like to become a better software designer, studying the evolution of great software designs will be more valuable than studying the great designs themselves. For it is in the evolution that the real wisdom lies. The structures that result from the evolution can help you, but without knowing why they were evolved into a design, you’re more likely to misapply them or over-engineer with them on your next project.
If you want to get the most out of patterns, you must do the same thing: See patterns in the context of refactoring, not just as reusable elements that exist apart from refactoring. This is my primary reason for producing a catalog of pattern-directed refactorings.
By learning to evolve your designs, you can become a better software designer and reduce the amount of work you over- or under-engineer. TDD and continuous refactoring are the key practices of evolutionary design. Instill pattern-directed refactorings into your knowledge of how to refactor and you’ll find yourself even better equipped to evolve great designs.
A refactoring is a “behavior-preserving transformation” or, as Martin Fowler defines it, “a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior”
The process of refactoring involves the removal of duplication, the simplification of complex logic, and the clarification of unclear code. When you refactor, you relentlessly poke and prod your code to improve its design. Such improvements may involve something as small as changing a variable name or as large as unifying two hierarchies.
Refactoring in small steps helps prevent the introduction of defects. Most refactorings take seconds or minutes to perform. Some large refactorings can require a sustained effort for days, weeks, or months until a transformation has been completed. Even such large refactorings are implemented in small steps.
It’s best to refactor continuously, rather than in phases. When you see code that needs improvement, improve it. On the other hand, if your manager needs you to finish a feature before a demo that just got scheduled for tomorrow, finish the feature and refactor later. Business is well served by continuous refactoring, yet the practice of refactoring must coexist harmoniously with business priorities.
- Make it easier to add new code.
When we add a new feature to a system, we have a choice: we can quickly program the feature without regard to how well it fits with an existing design, or we can modify the existing design so it can easily and gracefully accommodate the new feature. If we go with the former approach, we incur design debt, which can be paid down later by refactoring. If we go with the latter approach, we analyze what will need to change to best accommodate the new feature and then make whatever changes are necessary.
- Improve the design of existing code.
By continuously improving the design of code, we make it easier and easier to work with. This is in sharp contrast to what typically happens: little refactoring and a great deal of attention paid to expediently adding new features. Continuous refactoring involves constantly sniffing for coding smells and removing smells immediately after finding them.
- Gain a better understanding of code.
Sometimes we look at code and have no idea what it does or how it works. Even if someone could stand next to us and explain the code, the next person to look at it could also be totally confused. Is it best to write a comment for such code? No. If the code isn’t clear, it’s an odor that needs to be removed by refactoring, not by deodorizing the code with a comment.
- Make coding less annoying.
I’ve often wondered what propels me to refactor code. Sure, I can say that I refactor to remove duplication, to simplify or clarify the code. But what actually propels me to refactor? Emotions. I often refactor simply to make code less annoying to work with.
Story about “John Thompson Hatter”.
In his book Simple and Direct, Jacques Barzun explains that all good writing is based upon revision. Revision, he points out, means to re-see. John Thompson’s sign was gradually revised by his friends, who helped him remove duplicate words, simplify his language, and clarify his intent.
The same is true of code. To get the best refactoring results, you’ll want the help of many eyes. This is one reason why extreme programming suggests the practices of pair-programming and collective code ownership.
Any fool can write code that a computer can understand. Good programmers write code that humans can understand
november(20, 2005) vs
Keeping code clean is a lot like keeping a room clean. Once your room becomes a mess, it becomes harder to clean. The worse the mess becomes, the less you want to clean it.
Suppose you do one giant cleanup of your room. Now what? If you want your room to remain clean, you can’t leave things on the floor (like those socks) or allow books, magazines, glasses, or toys to pile up on tables. You must practice continuous hygiene.
To keep code clean, we must continuously remove duplication and simplify and clarify code. We must not tolerate messes in code, and we must not backslide into bad habits. Clean code leads to better design, which leads to faster development, which leads to happy customers and programmers. Keep your code clean.
As the class reassembled, I asked everyone what had gone wrong. The young programmer provided the answer: he had not taken small steps. By combining several refactorings into a single, large step, he thought he would go faster; but just the opposite was true. Each big step generated failures in the unit tests, which took a good deal of time to fix, not to mention that some of the fixes needed to be undone during later steps.
The technical language of refactoring doesn’t communicate effectively with the vast majority of management. Instead, Ward Cunningham’s financial metaphor of design debt works infinitely better. Design debt occurs when you don’t consistently do three things.
- Remove duplication
- Simplify your code
- Clarify you code’s intent
Few systems remain completely free of design debt. Wired as we are, humans just don’t write perfect code the first time around. We naturally accumulate design debt. So the question becomes, “When do you pay it down?”
In financial terms, if you don’t pay off a debt, you incur late fees. If you don’t pay your late fees, you incur higher late fees. The more you don’t pay, the worse your fees and payments become. Compound interest kicks in, and as time goes on, getting out of debt becomes an impossible dream. So it is with design debt.
Evolutionary design provides a better way. It suggests that you:
- Form one team
- Drive the framework from application needs
- Continuously improve applications and the framework by refactoring
Composite refactorings are high-level refactorings composed of low-level refactorings. Much of the work performed by low-level refactorings involves moving code around. For example, Extract Method moves code to a new method, Pull Up Method moves a method from a subclass to a superclass, Extract Class moves code to a new class, and Move Method moves a method from one class to another.
Testing also plays an altogether different role in refactoring; it can be used to rewrite and replace old code. A test-driven refactoring involves applying test-driven development to produce replacement code and then swap out old code for new code (while retaining and rerunning the old code’s tests).
- The describe an overall plan for a refactoring sequence
- They suggest nonobvious design directions
- They provide insights into implementing patterns
Beginning in the late 1980s, software practitioners with years of experience began studying Alexander’s works and sharing their knowledge in the form of patterns and intricate networks of patterns, known as pattern languages. This led to the publication of valuable papers and books about patterns and pattern languages in such areas as object-oriented design, analysis and domain design, process and organizational design, and user interface design.
Each pattern is a three-part rule, which expresses a relation between a certain context, a problem, and a solution
As an element in the world, each pattern is a relationship between a certain context, a certain system of forces which occurs repeatedly in that context, and a certain spatial configuration which allows these forces to resolve themselves.
As an element of language, a pattern is an instruction, which shows how this spatial configuration can be used, over and over again, to resolve the given system of forces, wherever the context makes it relevant.
The overuse of patterns tends to result from being patterns happy. We are patterns happy when we become so enamored of patterns that we simply must use them in our code. A patterns-happy programmer may work hard to use patterns on a system just to get the experience of implementing them or maybe to gain a reputation for writing really good, complex code.
It is perhaps impossible to avoid being patterns happy on the road to learning patterns. In fact, most of us learn by making mistakes. I’ve been patterns happy on more than one occasion.
The true joy of patterns comes from using them wisely. Refactoring helps us do that by focusing our attention on removing duplication, simplifying code, and making code communicate its intention. When patterns evolve into a system by means of refactoring, there is less chance of over-engineering with patterns. The better you get at refactoring, the more chance you’ll have to find the joy of patterns.
Every pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.
In fact, the authors of Design Patterns go to great pains to explain different ways to implement each pattern in a section called Implementation Notes. If you read the implementation notes for the Factory Method pattern, you’ll find that there are plenty of ways to implement a Factory Method.
The main point? There are many ways to implement a pattern.
Unfortunately, when programmers look at the solitary Structure diagram accompanying each pattern in the Design Patterns book, they often jump to the conclusion that the Structure diagram is the way to implement the pattern. If they’d only read the crucial implementation notes, they’d know better. Yet many a programmer picks up Design Patterns, gazes at a Structure diagram, and then begins coding. The result is code that exactly mirrors the Structure diagram, instead of a pattern implementation that best matches the need at hand.
It seems you can’t overemphasize that a pattern’s Structure diagram is just an example, not a specification. It portrays the implementation we see most often. As such the Structure diagram will probably have a lot in common with your own implementation, but differences are inevitable and actually desirable. At the very least you will rename the participants as appropriate for your domain. Vary the implementation trade-offs, and your implementation might start looking a lot different from the Structure diagram.
In general, pattern implementations ought to help remove duplicate code, simplify logic, communicate intention, and increase flexibility. However, as this story reveals, people’s familiarity with patterns plays a major role in how they perceive patterns-based refactorings. I prefer that teams learn patterns rather than avoid using them because the teams view patterns as being too complex. On the other hand, some pattern implementations can make code more complex than it needs to be; when that happens, backtracking or more refactoring is necessary.
If you don’t know patterns, you’re less likely to evolve great designs. Patterns capture wisdom. Reusing that wisdom is extremely useful.
Maynard Solomon, a biographer of Mozart, observed that Mozart didn’t invent new forms of music—he simply combined existing forms to produce stunningly good results. Patterns are like new forms of music you can use and combine to produce excellent software designs.
Yet as I mentioned at the start of this book, having knowledge of patterns isn’t enough to evolve great software. You must also know how to intelligently use patterns, which is the subject of this book. Nevertheless, if you don’t study patterns, you’ll lack access to important, even beautiful, design ideas.
Refactoring, or improving the design of existing code, requires that you know what code needs improvement. Catalogs of refactorings help you gain this knowledge, yet your situation may be different from what you see in a catalog. It’s therefore necessary to learn common design problems so you can recognize them in your own code.
The most common design problems result from code that
- Is duplicated
- Is unclear
- Is complicated
In their chapter “Bad Smells in Code” in Refactoring, Martin Fowler and Kent Beck provide additional guidance for identifying design problems. They liken design problems to smells and explain which refactorings, or combinations of refactorings, work best to eliminate odors.
Fowler and Beck’s code smells target problems that occur everywhere: in methods, classes, hierarchies, packages (namespaces, modules), and entire systems. The names of their smells, such as Feature Envy, Primitive Obsession, and Speculative Generality, provide a rich and colorful vocabulary with which programmers may rapidly communicate about design problems.
Duplicated code is the most pervasive and pungent smell in software. It tends to be either explicit or subtle. Explicit duplication exists in identical code, while subtle duplication exists in structures or processing steps that are outwardly different yet essentially the same.
You can often remove explicit and/or subtle duplication in subclasses of a hierarchy by applying Form Template Method. If a method in the subclasses is implemented similarly, except for an object creation step, applying Introduce Polymorphic Creation with Factory Method will pave the way for removing more duplication by means of a Template Method.
If the constructors of a class contain duplicated code, you can often eliminate the duplication by applying Chain Constructors.
If you have separate code for processing a single object or a collection of objects, you may be able to remove duplication by applying Replace One/Many Distinctions with Composite.
If subclasses of a hierarchy each implement their own Composite, the implementations may be identical, in which case you can use Extract Composite.
If you process objects differently merely because they have different interfaces, applying Unify Interfaces with Adapter will pave the way for removing duplicated processing logic.
If you have conditional logic to deal with an object when it is null and the same null logic is duplicated throughout your system, applying Introduce Null Object will eliminate the duplication and simplify the system.
In their description of this smell, Fowler and Beck explain several good reasons why short methods are superior to long methods. A principal reason involves the sharing of logic. Two long methods may very well contain duplicated code. Yet if you break those methods into smaller ones, you can often find ways for them to share logic.
When I’m faced with a long method, one of my first impulses is to break it down into a Composed Method by applying the refactoring Compose Method. This work usually involves applying Extract Method. If the code you’re transforming into a Composed Method accumulates information to a common variable, consider applying Move Accumulation to Collecting Parameter.
If your method is long because it contains a large switch statement for dispatching and handling requests, you can shrink the method by using Replace Conditional Dispatcher with Command.
If you use a switch statement to gather data from numerous classes with different interfaces, you can shrink the size of the method by applying Move Accumulation to Visitor.
If a method is long because it contains numerous versions of an algorithm and conditional logic to choose which version to use at runtime, you can shrink the size of the method by applying Replace Conditional Logic with Strategy.
Conditional logic is innocent in its infancy, when it is simple to understand and contained within a few lines of code. Unfortunately, it rarely ages well. For example, you implement several new features and suddenly your conditional logic becomes complicated and expansive. Several refactorings in Refactoring and this catalog address such problems.
If conditional logic controls which of several variants of a calculation to execute, consider applying Replace Conditional Logic with Strategy.
If conditional logic controls which of several pieces of special-case behavior must be executed in addition to the class’s core behavior, you may want to use Move Embellishment to Decorator.
If the conditional expressions that control an object’s state transitions are complex, consider simplifying the logic by applying Replace State-Altering Conditionals with State.
Dealing with null cases often leads to the creation of conditional logic. If the same null conditional logic is duplicated throughout your system, you can clean it up by using Introduce Null Object.
Primitives, which include integers, strings, doubles, arrays, and other low-level language elements, are generic because many people use them. Classes, on the other hand, may be as specific as you need them to be because you create them for specific purposes. In many cases, classes provide a simpler and more natural way to model things than primitives. In addition, once you create a class, you’ll often discover that other code in a system belongs in that class.
If a primitive value controls logic in a class and the primitive value isn’t type-safe (i.e., clients can assign it to an unsafe or incorrect value), consider applying Replace Type Code with Class. The result will be code that is type-safe and capable of being extended by new behavior (something you can’t do with a primitive).
If an object’s state transitions are controlled by complex conditional logic that uses primitive values, you can use Replace State-Altering Conditionals with State. The result will be numerous classes to represent each state and simplified state transition logic.
If complicated conditional logic controls which algorithm to run and that logic relies on primitive values, consider applying Replace Conditional Logic with Strategy.
If you implicitly create a tree structure using a primitive representation, such as a string, your code may be difficult to work with, prone to errors, and/or filled with duplication. Applying Replace Implicit Tree with Composite will reduce these problems.
If many methods of a class exist to support numerous combinations of primitive values, you may have an implicit language. If so, consider applying Replace Implicit Language with Interpreter.
If primitive values exist in a class only to provide embellishments to the class’s core responsibility, you may want to use Move Embellishment to Decorator.
Finally, even if you have a class, it may still be too primitive to make life easy for clients. This may be the case if you have a Composite implementation that is tricky to work with. You can simplify how clients build the Composite by applying Encapsulate Composite with Builder.
This smell indicates the lack of what David Parnas so famously termed “information hiding”. The smell occurs when methods or classes that ought not be visible to clients are publicly visible to them. Exposing such code means that clients know about code that is unimportant or only indirectly important. This contributes to the complexity of a design.
The refactoring Encapsulate Classes with Factory deodorizes this smell. Not every class that is useful to clients needs to be public (i.e., have a public constructor). Some classes ought to be referenced only via their common interfaces. You can make that happen if you make the class’s constructors non-public and use a Factory to produce instances.
When code and/or data used to perform a responsibility becomes sprawled across numerous classes, Solution Sprawl is in the air. This smell often results from quickly adding a feature to a system without spending enough time simplifying and consolidating the design to best accommodate the feature.
Solution Sprawl is the identical twin brother of Shotgun Surgery, a smell described by Fowler and Beck. You become aware of this smell when adding or updating a system feature causes you to make changes to many different pieces of code. Solution Sprawl and Shotgun Surgery address the same problem, yet are sensed differently. We become aware of Solution Sprawl by observing it, while we become aware of Shotgun Surgery by doing it.
Move Creation Knowledge to Factory is a refactoring that solves the problem of a sprawling object creation responsibility.
This Fowler and Beck coding smell occurs when the interfaces of two classes are different and yet the classes are quite similar. If you can find the similarities between the two classes, you can often refactor the classes to make them share a common interface.
However, sometimes you can’t directly change the interface of a class because you don’t have control over the code. The typical example is when you’re working with a third-party library. In that case, you can apply Unify Interfaces with Adapter to produce a common interface for the two classes.
When describing this smell, Fowler and Beck write, “A class that isn’t doing enough to pay for itself should be eliminated”. It’s not uncommon to encounter a Singleton that isn’t paying for itself. In fact, the Singleton may be costing you something by making your design too dependent on what amounts to global data. Inline Singleton explains a quick, humane procedure for eliminating a Singleton.
Fowler and Beck note that the presence of too many instance variables usually indicates that a class is trying to do too much. In general, large classes typically contain too many responsibilities. Extract Class and Extract Subclass, which are some of the main refactorings used to address this smell, help move responsibilities to other classes. The pattern-directed refactorings in this book make use of these refactorings to reduce the size of classes.
Replace Conditional Dispatcher with Command extracts behavior into Command classes, which can greatly reduce the size of a class that performs a variety of behaviors in response to different requests.
Replace State-Altering Conditionals with State can reduce a large class filled with state transition code into a small class that delegates to a family of State classes.
Replace Implicit Language with Interpreter can reduce a large class into a small one by transforming copious code for emulating a language into a small Interpreter.
Switch statements (or their equivalent, if…elseif…elseif… structures) aren’t inherently bad. They become bad only when they make your design more complicated or rigid than it needs to be. In that case, it’s best to refactor away from switch statements to a more object-based or polymorphic solution.
Replace Conditional Dispatcher with Command describes how to break down a large switch statement into a collection of Command objects, each of which may be looked up and invoked without relying on conditional logic.
Move Accumulation to Visitor describes an example where switch statements are used to obtain data from instances of classes that have different interfaces. By refactoring the code to use a Visitor, no conditional logic is needed and the design becomes more flexible.
This smell is a subtle form of duplication. It exists when you have numerous pieces of code that do the same thing using different kinds or quantities of data or objects.
For example, say you have numerous methods on a class for performing queries. Each of these methods performs a query using specific conditions and data. The more specialized queries you need to support, the more query methods you must create. Pretty soon you have an explosion of methods to handle the many ways of performing queries. You also have an implicit query language. You can remove all of these methods and the combinatorial explosion smell by applying Replace Implicit Language with Interpreter.
When a problem is solved one way throughout a system and the same problem is solved another way in the same system, one of the solutions is the oddball or inconsistent solution. The presence of this smell usually indicates subtly duplicated code.
To remove this duplication, first determine your preferred solution. In some cases, the solution used least often may be your preferred solution if it is better than the solution used most of the time. After determining your preferred solution, you can often apply Substitute Algorithm to produce a consistent solution throughout your system. Given a consistent solution, you may be able to move all instances of the solution to one place, thereby removing duplication.
The Oddball Solution smell is usually present when you have a preferred way to communicate with a set of classes, yet differences in the interfaces of the classes prevent you from communicating with them in a consistent way. In that case, consider applying Unify Interfaces with Adapter (247) to produce a common interface by which you may communicate consistently with all of the classes. Once you do that, you can often discover ways to remove duplicated processing logic.
The format of each refactoring in this book mostly follows the format used by Martin Fowler in Refactoring, with a few of my own embellishments. Each refactoring generally has most, if not all, of the following parts.
Name: The name is important for building up a vocabulary of refactorings. The refactorings in this book refer to numerous refactorings in Refactoring as well as refactorings in this book
Summary: Each refactoring in this book describes a design transformation. I use textual and diagrammatic descriptions to explain each transformation. I call the diagrammatic portion of the summary a sketch because it uses UML to show the essence of a design transformation. The sketches use a variety of UML diagrams, including class, object, collaboration, and sequence diagrams
Motivation: This section describes why you’d want to use this refactoring. It also tends to include high-level descriptions that give a good overview of the patterns
At the end of the Motivation section, I include a box that lists the benefits and liabilities associated with the refactoring. A plus sign (+) signifies a benefit, while a minus sign (–) signifies a liability.
e.g.: Replace State-Altering Conditionals with State
Benefits and Liabilities
- Reduces or removes state-changing conditional logic.
- Simplifies complex state-changing logic.
- Provides a good bird’s-eye view of state-changing logic.
– Complicates a design when state transition logic is already easy to follow.
Mechanics: This section lists the step-by-step instructions for implementing the refactoring. Some Mechanics sections include a preamble that describes what ought to be in place before you begin the refactoring. All of the Mechanics sections include numbered steps, so you can easily correlate these steps with the numbered steps in the Example sections
Example: This section is where I go into great depth about how I used the refactoring in question to transform a design. Each step within an Example section is numbered and correlates with the numbered steps in the Mechanics section for that refactoring
Variations: A few refactorings in this book include a section that explains a variation on the refactoring
The examples in this book either come from real-world projects or are inspired by real-world projects I’ve worked on. I use real-world code rather than toy code because when we refactor real-world code, our refactoring decisions are constrained by forces present in the code. This doesn’t happen as much with toy code. In addition, toy code, which tends to lack these forces or contain only a portion of them, never seems to offer as rich an educational experience as real-world code.
In a section called “How Mature Are These Refactorings?” Martin Fowler writes:
As you use the refactorings bear in mind that they are a starting point. You will doubtless find gaps in them. I’m publishing them now because although they are not perfect, I do believe they are useful. I believe they will give you a starting point that will improve your ability to refactor efficiently. That is what they do for me.
It’s best to treat my refactorings as a recipe that you adapt to your environment. This may mean skipping some steps in the mechanics of a refactoring, or it may mean going about the refactoring in a different way. What matters in the end is not the steps you follow but whether or not you improve the design of your code. If this book gives you useful ideas for improving your code, I’ll be happy.
This catalog does not describe all of the possible pattern-directed refactorings I could have included. After writing 27 refactorings, it seemed like it was time to ship a book. However, my hope is that other authors will help extend this catalog to document additional pattern-directed refactorings that will be useful to programmers.
While every object-oriented system creates objects or object structures, the creation code is not always free of duplication, simple, intuitive, or as loosely coupled to client code as it could be. The six refactorings in this chapter target design problems in everything from constructors to overly complicated construction logic to unnecessary Singletons. While these refactorings don’t address every creational design problem you’ll likely encounter, they do address some of the most common problems.
If there are too many constructors on a class, clients will have a difficult time knowing which constructor to call. One solution is to reduce the number of constructors by applying such refactorings as Extract Class or Extract Subclass. If that isn’t possible or useful, you can clarify the intention of the constructors by applying Replace Constructors with Creation Methods.
What is a Creation Method? It is simply a static or nonstatic method that creates and returns an object instance. A class that is a Factory is one that implements one or more Creation Methods. If data and/or code used in object creation become sprawled across numerous classes, you’ll likely find yourself frequently updating code in numerous places, a sure sign of the smell Solution Sprawl.
Constructors on a class make it hard to decide which constructor to call during development.
Replace the constructors with intention-revealing Creation Methods that return object instances
Some languages allow you to name constructors any way you like, regardless of the name of the class. Other languages, such as Java and C++, don’t allow this; each constructor must be named after its class. If you have one simple constructor, this may not be a problem. On the other hand, if you have multiple constructors, programmers will have to choose which constructor to call by studying the expected parameters and/or poking around at the constructor code. What’s wrong with that? A lot.
Constructors simply don’t communicate intention efficiently or effectively. The more constructors you have, the easier it is for programmers to choose the wrong one. Having to choose which constructor to call slows down development, and the code that does call one of the many constructors often fails to sufficiently communicate the nature of the object being constructed.
If you need to add a new constructor to a class with the same signature as an existing constructor, you’re out of luck. Because they have to share the same name, you can’t add the new constructor—since it isn’t possible to have two constructors with the same signature in the same class, despite the fact that they would create different kinds of objects.
It’s common, particularly on mature systems, to find numerous constructors that are no longer being used yet continue to live on in the code. Why are these dead constructors still present? Most of the time it’s because programmers don’t know that the constructors have no caller. Either they haven’t checked for callers (perhaps because the search expression they’d need to formulate is too complicated) or they aren’t using a development environment that automatically highlights uncalled code. Whatever the reason, dead constructors only bloat a class and make it more complicated than it needs to be.
A Creation Method can help make these problems go away. A Creation Method is simply a static or nonstatic method on a class that instantiates new instances of the class. There are no name constraints on Creation Methods, so you can name them to clearly express what you are creating (e.g.,
createRevolver()). This naming flexibility means that two differently named Creation Methods can accept the same number and type of arguments. And for programmers who lack modern development environments, it’s usually easier to find dead Creation Method code than it is to find dead constructor code because the search expressions on specifically named methods are easier to formulate than the search expressions on one of a group of constructors.
One liability of this refactoring is that it may introduce a nonstandard way to perform creation. If most of your classes are instantiated using new yet some are instantiated using a Creation Method, programmers will have to learn how creation gets done for each class. However, this nonstandard technique for creation may be a lesser evil than having classes with too many constructors.
After you have identified a class that has many constructors, it’s best to consider applying Extract Class or Extract Subclass before you decide to apply this refactoring. Extract Class is a good choice if the class in question is simply doing too much work (i.e., it has too many responsibilities). Extract Subclass is a good choice if instances of the class use only a small portion of the class’s instance variables.
- Communicates what kinds of instances are available better than constructors
- Bypasses constructor limitations, such as the inability to have two constructors with the same number and type of arguments
- Makes it easier to find unused creation code
– Makes creation nonstandard: some classes are instantiated using new, while others use Creation Methods
Before beginning this refactoring, identify the catch-all constructor, a full-featured constructor to which other constructors delegate their work. If you don’t have a catch-all constructor, create one by applying Chain Constructors.
- Find a client that calls a class’s constructor in order to create a kind of instance. Apply Extract Method on the constructor call to produce a public, static method. This new method is a creation method. Now apply Move Method to move the creation method to the class containing the chosen constructor
- Find all callers of the chosen constructor that instantiate the same kind of instance as the creation method and update them to call the creation method
- If the chosen constructor is chained to another constructor, make the creation method call the chained constructor instead of the chosen constructor. You can do this by inlining the constructor, a refactoring that resembles Inline Method
- Repeat steps 1–3 for every constructor on the class that you’d like to turn into a Creation Method
- If a constructor on the class has no callers outside the class, make it non-public
As you consider implementing Replace Constructors with Creation Methods, you may calculate in your head that you’d need something on the order of 50 Creation Methods to account for every object configuration supported by your class. Writing 50 methods doesn’t sound like much fun, so you may decide not to apply this refactoring. Keep in mind that there are other ways to handle this situation. First, you need not produce a Creation Method for every object configuration: you can write Creation Methods for the most popular configurations and leave some public constructors around to handle the rest of the cases. It also makes sense to consider using parameters to cut down on the number of Creation Methods.
Can too many Creation Methods on a class obscure its primary responsibility? This is really a matter of taste. Some folks find that when object creation begins to dominate the public interface of a class, the class no longer strongly communicates its main purpose. If you’re working with a class that has Creation Methods on it and you find that the Creation Methods distract you from the primary responsibilities of the class, you can refactor the related Creation Methods to a single Factory.
Data and code used to instantiate a class is sprawled across numerous classes.
Move the creation knowledge into a single Factory class
When the knowledge for creating an object is spread out across numerous classes, you have creation sprawl: the placement of creational responsibilities in classes that ought not to be playing any role in an object’s creation. Creation sprawl, which is a case of the Solution Sprawl smell, tends to result from an earlier design problem. For example, a client needed to configure an object based on some preferences yet lacked access to the object’s creation code. If the client can’t easily access the object’s creation code, say, because it exists in a system layer far removed from the client, how can the client configure the object?
A typical answer is by using brute force. The client passes its configuration preferences to one object, which hands them off to another object, which holds onto them until the creation code, by means of still more objects, obtains the information for use in configuring the object. While this works, it spreads creation code and data far and wide.
The Factory pattern is helpful in this context. It uses one class to encapsulate both creation logic and a client’s instantiation/configuration preferences. A client can tell a Factory instance how to instantiate/configure an object, and then the same Factory instance may be used at runtime to perform the instantiation/configuration.
- Consolidates creation logic and instantiation/configuration preferences
- Decouples a client from creation logic
– Complicates a design when direct instantiation would do
These mechanics assume your Factory will be implemented as a class, rather than as an interface implemented by a class. If you need a Factory interface that is implemented by a class, you must make minor modifications to these mechanics.
- An instantiator is a class that collaborates with other classes to instantiate a product (i.e., an instance of some class). If the instantiator doesn’t instantiate the product using a Creation Method, modify it and, if necessary, also modify the product’s class, so the instantiation occurs through a Creation Method
- Create a new class that will become your factory. Name your factory based on what it creates (e.g.,
- Apply Move Method to move the Creation Method to the factory. If the Creation Method is static, you can make it nonstatic after moving it to the factory
- Update the instantiator to instantiate the factory and call the factory to obtain an instance of the class
- Repeat this step for any instantiators that could no longer compile because of changes made during step 3
- Data and methods from the other classes are still being used in the instantiation. Move whatever makes sense into the factory, so it handles as much of the creation work as possible. This may involve moving where the factory gets instantiated and who instantiates it
Clients directly instantiate classes that reside in one package and implement a common interface.
Make the class constructors non-public and let clients create instances of them using a Factory
A client’s ability to directly instantiate classes is useful so long as the client needs to know about the very existence of those classes. But what if the client doesn’t need that knowledge? What if the classes live in one package and implement one interface, and those conditions aren’t likely to change? In that case, the classes in the package could be hidden from clients outside the package by giving a Factory the responsibility of creating and returning instances that implement a common interface.
The one major issue with this refactoring involves a dependency cycle: whenever you create a new subclass or add/modify an existing subclass constructor, you must add a new Creation Method to your Factory. If you don’t often add new subclasses or add/modify constructors to existing subclasses, this is not a problem. If you do, you may wish to avoid this refactoring or transition to a design that lets clients directly instantiate whichever subclasses they like. You can also consider a hybrid approach in which you produce a Factory for the most popular kinds of instances and don’t fully encapsulate all of the subclasses so that clients may instantiate classes as needed.
- Simplifies the creation of kinds of instances by making the set available through intention-revealing methods
- Reduces the “conceptual weight” of a package by hiding classes that don’t need to be public
- Helps enforce the mantra “program to an interface, not an implementation”
– Requires new/updated Creation Methods when new kinds of instances must be created
– Limits customization when clients can only access a Factory’s binary code, not its source code
In general, you’ll want to apply this refactoring when your classes share a common public interface, share the same superclass, and reside in the same package.
- Find a client that calls a class’s constructor in order to create a kind of instance. Apply Extract Method on the constructor call to produce a public, static method. This new method is a creation method. Now apply Move Method to move the creation method to the superclass of the class with the chosen constructor
- Find all callers of the chosen constructor that instantiate the same kind of instance as the creation method and update them to call the creation method
- Repeat steps 1 and 2 for any other kinds of instances that may be created by the class’s constructor
- Declare the class’s constructor to be non-public
- Repeat steps 1–4 for all classes you would like to encapsulate
Java’s java.util.Collections class contains a remarkable example of what encapsulating classes with Creation Methods is all about. The class’s author, Joshua Bloch, needed to give programmers a way to make collections, lists, sets, and maps unmodifiable and/or synchronized. Instead of creating public java.util Proxy classes (for handling synchronization and unmodifiabilty) and then expecting programmers to protect their own collections, he defined the proxies in the Collections class as non-public inner classes and then gave Collections a set of Creation Methods from which programmers could obtain the kinds of proxies they needed.
Classes in a hierarchy implement a method similarly, except for an object creation step.
Make a single superclass version of the method that calls a Factory Method to handle the instantiation
To form a Creation Method (see Replace Constructors with Creation Methods), a class must implement a static or nonstatic method that instantiates and returns an object. On the other hand, if you wish to form a Factory Method, you need the following:
- A type (defined by an interface, abstract class, or class) to identify the set of classes that Factory Method implementors may instantiate and return
- The set of classes that implement that type
- Several classes that implement the Factory Method, making local decisions about which of the set of classes to instantiate, initialize, and return
- Reduces duplication resulting from a custom object creation step
- Effectively communicates where creation occurs and how it may be overridden
- Enforces what type a class must implement to be used by a Factory Method
– May require you to pass unnecessary parameters to some Factory Method implementers
This refactoring is most commonly used in the following situations:
- When sibling subclasses implement a method similarly, except for an object creation step
- When a superclass and subclass implement a method similarly, except for an object creation step
The mechanics presented in this subsection handle the sibling subclasses scenario and can easily be adapted for the superclass and subclass scenario. For the purposes of these mechanics, a method that is implemented similarly in a hierarchy, except for an object creation step, will be called a similar method.
- In a subclass that contains a similar method, modify the method so the custom object creation occurs in what these steps will call an instantiation method. You’ll usually do this by applying Extract Method on the creation code or by refactoring the creation code to call a previously extracted instantiation method
- Use a generic name for the instantiation method (e.g.,
newProduct) because the same method name will need to be used in the sibling subclass’s similar methods. Make the return type for the instantiation method be the type that is common for the custom instantiation logic in the sibling subclass’s similar methods
- Use a generic name for the instantiation method (e.g.,
- Repeat step 1 for the similar method in the sibling subclasses. This should yield one instantiation method for each of the sibling’s subclasses, and the instantiation method’s signature should be the same in every sibling subclass
- Next, modify the superclass of the sibling subclasses. If you can’t modify that class or would rather not do so, apply Extract Superclass to produce a superclass that inherits from the superclass of the sibling subclasses and makes the sibling subclasses inherit from the new superclass
- The participant name for the superclass of the sibling subclasses is Factory Method: Creator
- Apply Form Template Method on the similar method. This will involve applying Pull Up Method. When you apply that refactoring, be sure to implement the following advice, which comes from a note in the mechanics for Pull Up Method:
- If you are in a strongly typed language and the [method you want to pull up] calls another method that is present on both subclasses but not the superclass, declare an abstract method on the superclass
- One such abstract method you’ll declare on the superclass will be for your instantiation method. Having declared that abstract method, you will have implemented a factory method. Each of the sibling subclasses is now a Factory Method: ConcreteCreator
- Repeat steps 1–4 if you have additional similar methods in the sibling subclasses that could benefit from calling the previously created factory method
- If the factory method in a majority of ConcreteCreators contains the same instantiation code, move that code to the superclass by transforming the factory method declaration in the superclass into a concrete factory method that performs the default (“majority case”) instantiation behavior
Building a Composite is repetitive, complicated, or error-prone.
Simplify the build by letting a Builder handle the details
A Builder performs burdensome or complicated construction steps on behalf of a client. A common motivation for refactoring to a Builder is to simplify client code that creates complex objects. When difficult or tedious parts of creation are implemented by a Builder, a client can direct the Builder’s creation work without having to know how that work is accomplished.
Builders often encapsulate Composites because the construction of Composites can frequently be repetitive, complicated, or error-prone. The authors of Design Patterns describe the intent of the Builder pattern as follows: “Separate the construction of a complex object from its internal representation so that the same construction process can create different representations”
While “creat[ing] different representations” of a complex object is a useful service, it isn’t the only service a Builder provides. As mentioned earlier, simplifying construction or decoupling client code from a complex object are also equally good reasons for using a Builder.
- Reduces the repetitive and error-prone nature of Composite creation
- Creates a loose coupling between client and Composite
- Allows for different representations of the encapsulated Composite or complex object
– May not have the most intention-revealing interface
The following mechanics assume you already have Composite-construction code and you’d like to encapsulate this code with a Builder.
- Create a builder, a new class that will become a Builder by the end of this refactoring. Make it possible for your builder to produce a one-node Composite. Add a method to the builder to obtain the result of its build
- Make the builder capable of building children. This often involves creating multiple methods for allowing clients to easily direct the creation and positioning of children
- If the Composite-construction code you’re replacing sets attributes or values on nodes, make the builder capable of setting those attributes and values
- Reflect on how simple your builder is for clients to use, and then make it simpler
- Refactor your Composite-construction code to use the new builder. This involves making your client code what is known in Design Patterns as a Builder: Client and Builder: Director
Code needs access to an object but doesn’t need a global point of access to it.
Move the Singleton’s features to a class that stores and provides access to the object. Delete the Singleton
Singletonitis, a term I coined, means “addiction to the Singleton pattern.” The intent of a Singleton is to “ensure a class only has one instance, and provide a global point of access to it”. You’re infected with Singletonitis when the Singleton pattern fixes itself so deeply into your skull that it begins lording it over other patterns and simpler design ideas, causing you to produce way too many Singletons.
When is a Singleton unnecessary?
Short answer: Most of the time.
Long answer: A Singleton is unnecessary when it’s simpler to pass an object resource as a reference to the objects that need it, rather than letting objects access the resource globally. A Singleton is unnecessary when it’s used to obtain insignificant memory or performance improvements. A Singleton isn’t necessary when code deep down in a layer of a system needs to access a resource but the code doesn’t belong in that layer to begin with. I could go on. The point is that Singletons aren’t necessary when you can design or redesign to avoid using them.
- Makes object collaborations more visible and explicit
- Requires no special code to protect a single instance
– Complicates a design when passing an object instance through many layers is awkward or difficult
This refactoring’s mechanics are identical to those of Inline Class. In the following steps, an absorbing class is one that will take on the responsibilities of the inlined Singleton.
- Declare the Singleton’s public methods on your absorbing class. Make the new methods delegate back to the Singleton, and remove any “static” designations they may have (in the absorbing class)
- If your absorbing class is itself a Singleton, you’ll want to keep the “static” designations for the methods
- Change all client code references to the Singleton to references to the absorbing class
- Use Move Method and Move Field to move features from the Singleton to the absorbing class until there is nothing left
- As in step 1, if your absorbing class is not a Singleton itself, remove any “static” designations from the methods and fields you move
- Delete the Singleton
Much of the code we write doesn’t start out being simple. To make it simple, we must reflect on what isn’t simple about it and continually ask, “How could it be simpler?” We can often simplify code by considering a completely different solution. The refactorings in this chapter present different solutions for simplifying methods, state transitions, and tree structures
Compose Method is about producing methods that efficiently communicate what they do and how they do what they do. A Composed Method consists of calls to well-named methods that are all at the same level of detail. If you want to keep your system simple, endeavor to apply Compose Method everywhere.
Algorithms often become complex as they begin to support many variations. Replace Conditional Logic with Strategy shows how to simplify algorithms by breaking them up into separate classes. Of course, if your algorithm isn’t sufficiently complicated to justify a Strategy, refactoring to one will only complicate your design.
You probably won’t refactor to a Decorator frequently. Yet it is a great simplification tool for a certain situation: when you have too much special-case or embellishment logic in a class. Move Embellishment to Decorator describes how to identify when you really need a Decorator and then shows how to separate embellishments from the core responsibility of a class.
Logic for controlling state transitions is notorious for becoming complex. This is especially true as you add more and more state transitions to a class. The refactoring Replace State-Altering Conditionals with State describes how to drastically simplify complex state transition logic and helps you determine whether your logic is complex enough to require a State implementation.
Replace Implicit Tree with Composite is a refactoring that targets the complexity of building and working with tree structures. It shows how a Composite can simplify a client’s creation and interaction with a tree structure.
The Command pattern is useful for simplifying certain types of code. The refactoring Replace Conditional Dispatcher with Command shows how this pattern can completely simplify a switch statement that controls which chunk of behavior to execute.
You can’t rapidly understand a method’s logic.
Transform the logic into a small number of intention-revealing steps at the same level of detail
A Composed Method is composed of calls to other methods. Good Composed Methods have code at the same level of detail. For example, the code set in bold in the following listing is not at the same level of detail as the nonbold code:
By refactoring to a Composed Method, all of the methods called within the paintCard() method are now at the same level of detail:
If you apply this refactoring on numerous methods within the same class, you may find that the class has an overabundance of small, private methods. In that case, you may see an opportunity to apply Extract Class.
Another possible downside of this refactoring involves debugging. If you debug a Composed Method, it can become difficult to find where the actual work gets done because the logic is spread out across many small methods.
A Composed Method’s name communicates what it does, while its body communicates how it does what it does. This allows you to rapidly comprehend the code in a Composed Method. When you add up all the time you and your team spend trying to understand a system’s code, you can just imagine how much more efficient and effective you’ll be if the system is composed of many Composed Methods.
- Efficiently communicates what a method does and how it does what it does
- Simplifies a method by breaking it up into well-named chunks of behavior at the same level of detail
– Can lead to an overabundance of small methods
– Can make debugging difficult because logic is spread out across many small methods
This is one of the most important refactorings I know. Conceptually, it is also one of the simplest—so you’d think that this refactoring would lead to a simple set of mechanics. In fact, it’s just the opposite. While the steps themselves aren’t complex, there is no simple, repeatable set of steps. Instead, there are guidelines for refactoring to Composed Method, some of which include the following.
- Think small: Composed Methods are rarely more than ten lines of code and are usually about five lines
- Remove duplication and dead code: Reduce the amount of code in the method by getting rid of blatant and/or subtle code duplication or code that isn’t being used
- Communicate intention: Name your variables, methods, and parameters clearly so they communicate their purposes (e.g.,
public void addChildTo(Node parent))
- Simplify: Transform your code so it’s as simple as possible. Do this by questioning how you’ve coded something and by experimenting with alternatives
- Use the same level of detail: When you break up one method into chunks of behavior, make the chunks operate at similar levels of detail. For example, if you have a piece of detailed conditional logic mixed in with some high-level method calls, you have code at different levels of detail. Push the detail down into a well-named method, at the same level of detail as the other methods in the Composed Method
Conditional logic in a method controls which of several variants of a calculation are executed.
Create a Strategy for each variant and make the method delegate the calculation to a Strategy instance
“Simplifying Conditional Expressions” is a chapter in Refactoring that contains over a half-dozen highly useful refactorings devoted to cleaning up conditional complexity. About Decompose Conditional, Martin Fowler writes, “One of the most common areas of complexity in a program lies in complex conditional logic”. We often find such logic in algorithms because they tend to grow, becoming more sophisticated over time. The Strategy pattern helps manage the complexity that results from having numerous variations of an algorithm.
Conditional logic is often used to decide which variation of an algorithm to use. Refactorings like Decompose Conditional or Compose Method can simplify such code. On the other hand, they can also overrun your host class with small methods that apply only to specific variations of the algorithm, thereby complicating the host class. In such a situation, it may be better to move each algorithm variation to new classes or to subclasses of the host class. This essentially involves choosing between object composition and inheritance.
- Clarifies algorithms by decreasing or removing conditional logic
- Simplifies a class by moving variations on an algorithm to a hierarchy
- Enables one algorithm to be swapped for another at runtime
– Complicates a design when an inheritance-based solution or a refactoring from “Simplifying Conditional Expressions” is simpler
– Complicates how algorithms obtain or receive data from their context class
Identify the context, a class with a calculation method that contains a lot of conditional logic.
- Create a strategy, a concrete class (known as Strategy:ConcreteStrategy in Design Patterns) that will become a genuine ConcreteStrategy by the end of this refactoring. Name your strategy after the behavior performed by the calculation method. You can append the word “Strategy” to the class name if you find it helps communicate the purpose of this new type
- Apply Move Method to move the calculation method to the strategy. When you perform this step, retain a version of the calculation method on the context that delegates to the strategy’s calculation method. Implementing this delegation will involve defining and instantiating a delegate, a field in context that holds a reference to a strategy
- Since most strategies require data in order to do their calculations, you’ll need to determine how to make that data available to the strategy. Here are two common approaches
a. Pass the context as a parameter to the strategy’s constructor or calculation method
b. Pass the necessary data from the context to the strategy via calculation method parameters
- Since most strategies require data in order to do their calculations, you’ll need to determine how to make that data available to the strategy. Here are two common approaches
- Let clients outfit a context with an instance of the strategy by applying Extract Parameter on the context code that instantiates a concrete strategy and assigns it to the delegate
- Apply Replace Conditional with Polymorphism on the strategy’s calculation method. To apply that refactoring, you’ll first be given a choice to use Replace Type Code with Subclasses or Replace Type Code with State/Strategy. Choose the former. You’ll need to implement Replace Type Code with Subclasses whether you have an explicit type code or not. If conditional logic in the calculation method identifies particular types of the calculation, use that conditional logic in place of explicit types as you work through the mechanics for Replace Type Code with Subclasses
Code provides an embellishment to a class’s core responsibility.
Move the embellishment code to a Decorator
When new features in a system are needed, it’s common to add new code to old classes. Such new code often embellishes the core responsibility or primary behavior of an existing class. The trouble with some of these embellishments is that they complicate their host classes with new fields, new methods, and new logic, all of which exists for special-case behavior that needs to be executed only some of the time.
The Decorator pattern offers a good remedy: place each embellishment in its own class and let that class wrap the type of object it needs to embellish so that clients may wrap the embellishment around objects at runtime, when special-case behavior is needed.
Decorator is not a pattern you’d refactor to if you wanted to move an embellishment out of a class with dozens of public methods. Why? Because a Decorator must be what Design Patterns calls a “transparent enclosure”: it must implement all of the public methods of the classes it decorates (and that would require a lot of useless code for a class with dozens of public methods). Transparent enclosures wrap objects in a way that is transparent to the objects being wrapped (i.e., a decoratee doesn’t know it has been decorated).
Refactoring embellishments out of a class can produce a design that is simple to those who are comfortable with object composition. To those who aren’t, code that used to be in one class is now spread out across many classes. Such a separation may make code harder to understand since because it no longer resides in one place. In addition, having code reside in different objects can make it harder to debug because debugging sessions must go through one or more Decorators before getting to a decorated object. In short, if a team isn’t comfortable with using object composition to “decorate” objects, the team may not be ready for this pattern.
- Simplifies a class by removing embellishments from it
- Effectively distinguishes a class’s core responsibility from its embellishments
- Helps remove duplicated embellishment logic in several related classes
– Changes the object identity of a decorated object
– Can make code harder to understand and debug
– Complicates a design when combinations of Decorators can adversely affect one another
Before you begin this refactoring, you must identify an embellished class, a class that contains an embellishment to its core responsibility. Not every class with an embellishment to its core responsibility will be a good candidate for being “decorated.” First ensure that the set of public methods that your Decorator will need to implement isn’t too large. Because a Decorator forms a “transparent enclosure” around the objects it decorates, a piece of client code should be able to call the same set of public methods on a decorated object as it would normally call on the object itself. If your embellished class declares and inherits lots of public methods, either reduce that number (by deleting, moving, or changing the visibility of methods) or consider applying a different refactoring, such as Replace Conditional Logic with Strategy.
- Identify or create an enclosure type, an interface or class that declares all of the public methods needed by clients of the embellished class. An enclosure type is known as Decorator: Component in Design Patterns
- If you already have an enclosure type, it’s likely to be an interface implemented by the embellished class or a superclass of the embellished class. Classes that contain state aren’t good enclosure types because Decorators will inherit their state when they don’t need to. If you don’t already have a proper enclosure type, create one by applying Unify Interfaces and/or Extract Interface
- Find the conditional logic (a switch or if statement) that adds the embellishment to your embellished class and remove that logic by applying Replace Conditional with Polymorphism
- Step 2 produced one or more subclasses of the embellished class. Transform these subclasses into delegating classes by applying Replace Inheritance with Delegation. When implementing this refactoring, be sure to do the following
- Make each delegating class implement the enclosure type
- Make the type for the delegating class’s delegate field be the enclosure type
- Decide whether your embellishment code will execute before or after your delegating class makes its call to the delegate
- Each delegating class now assigns its delegate field to a new instance of the embellished class. Ensure that this assignment logic exists in a delegating class constructor. Then extract the part of the assignment statement that instantiates the embellished class into a parameter by applying Extract Parameter. If possible, remove any unnecessary constructor parameters by repeatedly applying Remove Parameter
The conditional expressions that control an object’s state transitions are complex.
Replace the conditionals with State classes that handle specific states and transitions between them
The primary reason for refactoring to the State pattern is to tame overly complex state-altering conditional logic. Such logic, which tends to spread itself throughout a class, controls an object’s state, including how states transition to other states. When you implement the State pattern, you create classes that represent specific states of an object and the transitions between those states. The object that has its state changed is known in Design Patterns as the context. A context delegates state-dependent behavior to a state object. State objects make state transitions at runtime by making the context point to a different state object.
Moving state-altering conditional logic out of one class and into a family of classes that represent different states can yield a simpler design that provides a better bird’s-eye view of the transitions between states. On the other hand, if you can easily understand the state transition logic in a class, you likely don’t need to refactor to the State pattern (unless you plan to add many more state transitions in the future).
- Reduces or removes state-changing conditional logic
- Simplifies complex state-changing logic
- Provides a good bird’s-eye view of state-changing logic
– Complicates a design when state transition logic is already easy to follow
- The context class is a class that contains the original state field, a field that gets assigned to or compared against a family of constants during state transitions. Apply Replace Type Code with Class on the original state field such that its type becomes a class. We’ll call that new class the state superclass
- Each constant in the state superclass now refers to an instance of the state superclass. Apply Extract Subclass to produce one subclass (known as State: ConcreteState) per constant, then update the constants in the state superclass so that each refers to the correct subclass instance of the state superclass. Finally, declare the state superclass to be abstract
- Find a context class method that changes the value of the original state field based on state transition logic. Copy this method to the state superclass, making the simplest changes possible to make the new method work. (A common, simple change is to pass the context class to the method in order to have code call methods on the context class.) Finally, replace the body of the context class method with a delegation call to the new method
- Choose a state that the context class can enter, and identify which state superclass methods make this state transition to other states. Copy the identified method(s), if any, to the subclass associated with the chosen state and remove all unrelated logic
- Delete the bodies of each of the methods copied to the state superclass during step 3 to produce an empty implementation for each method
You implicitly form a tree structure, using a primitive representation, such as a String.
Replace your primitive representation with a Composite
Data or code forms an implicit tree when it’s not explicitly structured as a tree but may be represented as a tree. For example, the code that creates the XML data in the previous code sketch outputs values like this:
Conditional logic can also form an implicit tree. Consider the conditional logic in the following code, which queries products from a repository:
- Encapsulates repetitive instructions like formatting, adding, or removing nodes
- Provides a generalized way to handle a proliferation of similar logic
- Simplifies construction responsibilities of a client
– Complicates a design when it’s simpler to construct implicit trees
The mechanics presented in this section feature two paths for implementing this refactoring. One path, which is standard throughout the book, involves applying refactorings on an implicit tree to gradually refactor it to a Composite, while the other way involves performing test-driven development to gradually refactor the implicit tree to a Composite.
- Identify an implicit leaf, a part of the implicit tree that could be modeled with a new class. This new class will be a leaf node (called Composite:Leaf in Design Patterns). Create a leaf node class by applying refactorings like Extract Class or by doing test-driven development—whichever is easier given your context
- Replace every occurrence of the implicit leaf with an instance of the leaf node, such that the implicit tree now relies on the leaf node instead of the implicit leaf
- Repeat steps 1 and 2 for any additional parts of the implicit tree that represent an implicit leaf. Make sure that all leaf nodes you create share a common interface. You can create this interface by applying Extract Superclass or Extract Interface
- Identify an implicit parent, a part of the implicit tree that acts as a parent to implicit leaves. The implicit parent will become a parent node class (called Composite). Develop this class by applying refactorings or doing test-driven development—again, use whichever approach is easier in your context
- Clients must be able to add leaf nodes to the parent node either through a constructor or an
add(…)method. The parent node must treat all children identically (i.e., via their common interface). The parent node may or may not implement the common interface. If clients must be able to add parent nodes to parent nodes (as is mentioned in step 6) or if you don’t want client code to distinguish between a leaf node and a parent node (as is the motivation for Replace One/Many Distinctions with Composite), make the parent node implement the common interface
- Clients must be able to add leaf nodes to the parent node either through a constructor or an
- Replace every occurrence of the implicit parent with code that uses a parent node instance, outfitted with the correct leaf node instances
- Repeat steps 4 and 5 for all additional implicit parents. Make it possible to add a parent node to a parent node only if your implicit parents support similar behavior
Conditional logic is used to dispatch requests and execute actions.
Create a Command for each action. Store the Commands in a collection and replace the conditional logic with code to fetch and execute Commands
Many systems receive, route, and handle requests. A conditional dispatcher is a conditional statement (such as a
switch) that performs request routing and handling. Some conditional dispatchers are well suited for their jobs; others aren’t.
Conditional dispatchers that are well suited for their jobs tend to route a small number of requests to small chunks of handler logic. Such dispatchers can often be viewed on a monitor without having to scroll to see all of the code. The Command pattern usually doesn’t provide a useful replacement for these kinds of conditional dispatchers.
On the other hand, if your conditional dispatcher is small, it may still not be a good fit for your system. The two most common reasons to refactor from a conditional dispatcher to a Command-based solution are the following.
- Not enough runtime flexibility: Clients that rely on the conditional dispatcher develop a need to dynamically configure it with new requests or handler logic. Yet the conditional dispatcher doesn’t allow for such dynamic configurations because all of its routing and handling logic is hard-coded into a single conditional statement
- A bloated body of code: Some conditional dispatchers become enormous and unwieldy as they evolve to handle new requests or as their handler logic becomes ever more complex with new responsibilities. Extracting the handler logic into different methods doesn’t help enough because the class that contains the dispatcher and extracted handler methods is still too large to work with
The Command pattern provides an excellent solution to such problems. To implement it, you simply place each piece of request-handling logic in a separate “command” class that has a common method, like
run(), for executing its encapsulated handler logic. Once you have a family of such commands, you can use a collection to store and retrieve instances of them; add, remove, or change instances; and execute instances by invoking their execution methods.
Routing requests and executing diverse behavior in a uniform way may be so central to a design that you may find yourself using the Command pattern early, rather than refactoring to it later. Many of the server-side, Web-based systems I’ve built have used the Command pattern to produce a standard way to route requests, execute actions, or forward actions to other actions. The Example section shows how to refactor to such a solution.
- Provides a simple mechanism for executing diverse behavior in a uniform way
- Enables runtime changes regarding which requests are handled and how
- Requires trivial code to implement
– Complicates a design when a conditional dispatcher is sufficient
- On a class containing a conditional dispatcher, find code that handles a request and apply Extract Method on that code until you have an execution method, a method that invokes the code’s behavior
- Repeat step 1 to extract all remaining chunks of request-handling code into execution methods
- Apply Extract Class on each execution method to produce a concrete command, a class that handles a request. This step usually implies making the execution method on the concrete command public. If the execution method in the new concrete command is too large or not quickly understandable, apply Compose Method
- Define a command, an interface or abstract class that declares an execution method that is the same for every concrete command. To implement this step, you’ll need to analyze your concrete commands to learn what’s unique or similar about them. Find answers to the following questions:
- What parameter(s) must be passed to a common execution method
- What parameter(s) could be passed during a concrete command’s construction
- What information could a concrete command obtain by calling back on a parameter, rather than having data passed direcly to the concrete command
- What is the simplest signature for an execution method that is the same for every concrete command
- Make every concrete command implement or extend your command and update all client code to work with each concrete command via the command type
- On the class that contains the conditional dispatcher, define and populate a command map, a map that contains instances of each concrete command, keyed by a unique identifier (e.g., a command name) that may be used at runtime to fetch a command
- On the class that contains the conditional dispatcher, replace the conditional code for dispatching requests with code to fetch the correct concrete command and execute it by calling its execution method. This class is now an Invoker
Generalization is the transformation of specific code into general-purpose code. The production of generalized code frequently occurs as a result of refactoring. All seven refactorings in this chapter yield generalized code. The most common motivation for applying them is to remove duplicated code. A secondary motivation is to simplify or clarify code.
Form Template Method helps remove duplication in similar methods of subclasses in a hierarchy. If the methods perform roughly the same steps, in the same order, yet the steps are slightly different, you can separate what varies from what is generic by producing a superclass method known as a Template Method.
Extract Composite is an application of the refactoring Extract Superclass. It’s applicable when a Composite has been implemented in multiple subclasses of a hierarchy with no good reason. By extracting a Composite to a superclass, the subclasses share one generic implementation of the Composite.
If you have some code for handling one object and separate code for handling a group of the same objects (usually in some collection), Replace One/Many Distinctions with Composite will help you produce a generic solution that handles one or many objects without distinguishing between the two.
Replace Hard-Coded Notifications with Observer is a classic example of replacing a specific solution with a general one. In this case, there is a tight coupling between objects that notify and objects that get notified. To allow instances of other classes to be notified, the code can be refactored to use an Observer.
An Adapter provides another way to unify interfaces. When clients communicate with similar classes using different interfaces, there tends to be duplicated processing logic. By applying Unify Interfaces with Adapter, clients may interact with similar classes using a generic interface. This tends to pave the way for other refactorings to remove duplicated process logic in the client code.
When a class acts as an Adapter for multiple versions of a component, library, API, or other entity, the class usually contains duplication and often lacks a simple design. Applying Extract Adapter produces classes that implement a common interface and adapt a single version of some code.
The final refactoring in this chapter, Replace Implicit Language with Interpreter, targets code that would be better designed were it to use an explicit language. Such code often uses numerous methods to accomplish what a language can do, only in a far more primitive and repetitive way. Refactoring such code to an Interpreter can yield a general-purpose solution that is more compact, simple, and flexible.
Two methods in subclasses perform similar steps in the same order, yet the steps are different.
Generalize the methods by extracting their steps into methods with identical signatures, then pull up the generalized methods to form a Template Method
Template Methods “implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary”. When invariant and variant behaviors are mixed together in the subclass implementations of a method, the invariant behavior is duplicated in the subclasses. Refactoring to a Template Method helps rid subclasses of their duplicated invariant behavior by moving the behavior to one place: a generalized algorithm in a superclass method.
A Template Method’s invariant behavior consists of the following:
- Methods called and the ordering of those methods
- Abstract methods that subclasses must override
- Hook methods (i.e., concrete methods) that subclasses may override
Because it is too tedious to implement many methods just to flesh out a Template Method in a subclass, the authors of Design Patterns suggest that a Template Method should minimize the number of abstract methods classes must override. There’s also no simple way for programmers to know which methods may be overridden (i.e., hook methods) without studying the contents of a Template Method.
- Removes duplicated code in subclasses by moving invariant behavior to a superclass
- Simplifies and effectively communicates the steps of a general algorithm
- Allows subclasses to easily customize an algorithm
- Complicates a design when subclasses must implement many methods to flesh out the algorithm
- In a hierarchy, find a similar method (a method in a subclass that performs similar steps in a similar order to a method in another subclass). Apply Compose Method on the similar method (in both subclasses), extracting identical methods (methods that have the same signature and body in each subclass) and unique methods (methods that have a different signature and body in each subclass)
- When deciding whether to extract code as a unique method or an identical method, consider this: If you extract the code as a unique method, you’ll eventually (during step 5) need to produce an abstract or concrete version of that unique method in the superclass. Will it make sense for subclasses to inherit or override the unique method? If not, extract the code into an identical method
- Pull up the identical methods to the superclass by applying Pull Up Method
- To produce an identical body for each version of the similar method, apply Rename Method on every unique method until the similar method is identical in each subclass
- If the similar method doesn’t already have an identical signature in each subclass, apply Rename Method to produce an identical signature
- Apply Pull Up Method on the similar method (in either subclass), defining abstract methods on the superclass for each unique method. The pulled-up similar method is now a Template Method
Subclasses in a hierarchy implement the same Composite.
Extract a superclass that implements the Composite
In Extract Superclass, Martin Fowler explains that if you have two or more classes with similar features, it makes sense to move the common features to a superclass. This refactoring is similar: it addresses the case when the similar feature is a Composite that would be better off in a superclass.
Subclasses in hierarchies that store collections of children and have methods for reporting information about those children are common. When the children being collected happen to be classes in the same hierarchy, there’s a good chance that much duplicate code can be removed by refactoring to Composite.
- Eliminates duplicated child-storage and child-handling logic
- Effectively communicates that child-handling logic may be inherited
- Create a composite, a class that will become a Composite during this refactoring. Name this class to reflect what kind of children it will contain (e.g.,
- Make each child container (a class in the hierarchy that contains duplicate child-handling code) a subclass of your composite
- In a child container, find a child-processing method that is purely duplicated or partially duplicated across the child containers. A purely duplicated method has the same method body with the same or different method names across child containers. A partially duplicated method has a method body with common and uncommon code and the same or different method names across child containers
- Whether you’ve found a purely duplicated or partially duplicated method, if its name isn’t consistent across child containers, make it consistent by applying Rename Method
- For a purely duplicated method, move the child collection field referenced by the method to your composite by applying Pull Up Field. Rename this field if its name doesn’t make sense for all child containers. Now move the method to the composite by applying Pull Up Method. If the pulled-up method relies on constructor code still residing in child containers, pull up that code to the composite’s constructor
- For a partially duplicated method, see if the method body can be made consistent across all child containers by using Substitute Algorithm. If so, refactor it as a purely duplicated method. Otherwise, extract the code that is common across all child-container implementations by using Extract Method and pull it up to the composite by using Pull Up Method. If the method body follows the same sequence of steps, some of which are implemented differently, see if you can apply Form Template Method
- Repeat step 3 for child-processing methods in the child containers that contain purely duplicated or partially duplicated code
- Check each client of each child container to see if it can now communicate with the child container using the composite interface. If it can, make it do so
A class processes single and multiple objects using separate pieces of code.
Use a Composite to produce one piece of code capable of handling single or multiple objects
When a class has a method for processing one object and a nearly identical method for processing a collection of the objects, a one/many distinction exists. Such a distinction can result in problems such as the following.
Duplicated code: Because the method that processes one object does the same thing as the method that processes a collection of the objects, duplicated code is often spread across the two methods. It’s possible to reduce this duplication without implementing a Composite, yet even if duplication is reduced, there are still two methods performing the same kind of processing
Nonuniform client code: Whether they have single objects or collections of objects, clients want their objects processed in one way. Yet the existence of two processing methods with different signatures forces clients to pass different kinds of data to the methods (i.e., one object or a collection of objects). This makes client code nonuniform, which reduces simplicity
Merging of results: The best way to explain this is with an example. Say you want to find all products that are red and priced under $5.00 or blue and priced above $10.00. One way to find these products is to call a
selectBy(List specs)method, which returns a List of results. Here’s an example call to
The main problem with
selectBy(List specs) is that it can’t handle an OR condition. So if you want to find all products that are red and under $5.00 or blue and above $10.00, you have to make separate calls to
selectBy(…) and then merge the results:
As you can see, this approach is awkward and verbose.
The Composite pattern provides a better way. It lets clients process one or many objects with a single method. This has many benefits.
- There’s no duplicated code across methods because only one method handles the objects, whether one or many
- Clients communicate with that method in a uniform way
- Clients can make one call to obtain the results of processing a tree of objects rather than having to make several calls and merging processed results. For example, to find red products under $5.00 or blue products above $10.00, a client creates and passes the following Composite to the processing method
In short, replacing a one/many distinction with a Composite is a way to remove duplication, make client calls uniform, and support the processing of trees of objects. However, if these latter two particular benefits aren’t all that important to your system and you can reduce most of the duplication in methods that have a one/many distinction, a Composite implementation could be overkill.
One common downside of the Composite pattern relates to type safety. To prevent clients from adding invalid objects to a Composite, the Composite code must contain runtime checks of the objects that clients attempt to add to it. This problem is also present with collections because clients can add invalid objects to collections as well.
- Removes duplicate code associated with handling one or many objects
- Provides a uniform way to process one or many objects
- Supports richer ways to process many objects (e.g., an OR expression)
– May require runtime checks for type safety during construction of the Composite
In this section and the Example section, a method that works with one object is called a one-object method while a method that works with a collection of objects is called a many-object method.
- The many-object method accepts a collection as a parameter. Create a new class that accepts the collection in a constructor and provides a getter method for it. This composite class will become what is known as a Composite in Design Patterns
- Apply Extract Method on the code within the many-object method that works with the collection. Make the extracted method public. Then apply Move Method on the extracted method to move it to your composite
- The many-object method will now be nearly identical to the one-object method. The main difference is that the many-object method instantiates your composite. If there are other differences, refactor to eliminate them
- Change the many-object method so it contains one line of code: a call to the one-object method that passes it your composite instance as an argument. You’ll need to make the composite share the same interface or superclass as the type used by the one-object method
- Because the many-object method now consists of just one line of code, it can be inlined by applying Inline Method
- Apply Encapsulate Collection on your composite. This will produce an
add(…)method on the composite, which clients will call instead of passing a collection to the composite’s constructor. In addition, the getter method for the collection will now return an unmodifiable collection
Subclasses are hard-coded to notify a single instance of another class.
Remove the subclasses by making their superclass capable of notifying one or more instances of any class that implements an Observer interface
Knowing when to refactor to an Observer first involves understanding when you don’t need an Observer. Consider the case of a single instance of a class called
Receiver that changes when an instance of a class called
Notifier changes, as shown in the following diagram.
In this case, the
Notifier instance holds onto a
Receiver reference and is hard-coded to notify that reference when it receives new information. Such a tight coupling between
Receiver makes sense when one
Notifier instance must notify only one
Receiver instance. If that circumstance changes and a
Notifier instance must notify numerous
Receiver instances, or instances of other classes, the design must evolve. This is exactly what occurred on Kent Beck and Erich Gamma’s JUnit framework. When users of the framework needed more than one party to observe changes to a
TestResult instance, a hard-coded notification was refactored to use the Observer pattern (see the Example section for details).
- Loosely couples a subject with its observers
- Supports one or many observers
– Complicates a design when a hard-coded notification will suffice
– Complicates a design when you have cascading notifications
– May cause memory leaks when observers aren’t removed from their subjects
A notifier is a class that references and sends notifications to another class. A receiver is a class that registers itself with a notifier and receives messages from the notifier. This refactoring details the steps for eliminating unnecessary notifiers by making their superclass a subject (known in Design Patterns as a ConcreteSubject) and transforming receivers into observers (known in Design Patterns as ConcreteObservers).
- If a notifier performs custom behavior on behalf of its receiver, instead of performing pure notification logic, move that behavior to the notifier’s receiver by applying Move Method. When finished, the notifier contains only notification methods (methods that notify a receiver)
- Produce an observer interface by applying Extract Interface on a receiver, selecting only those methods called by its notifier. If other notifiers call receiver methods not on the observer interface, add those methods to the observer interface so that it will work for all receivers
- Make every receiver implement the observer interface. Then make every notifier communicate with its receiver exclusively through the observer interface. Every receiver is now an observer
- Choose a notifier and apply Pull Up Method on its notification methods. This includes pulling up the notifier’s observer interface reference as well as code for setting that reference. The notifier’s superclass is now the subject
- Update each notifier’s observer to register and communicate with the subject, instead of the notifier, and then delete the notifier
- Refactor the subject so it holds onto a collection of observers, rather than just one. This includes updating the way observers register themselves with their subject. It’s common to create a method on the subject for adding observers (e.g.,
addObserver(Observer observer)). Finally, update the subject so its notification methods notify all observers in its collection of observers
Clients interact with two classes, one of which has a preferred interface.
Unify the interfaces with an Adapter
Refactoring to an Adapter is useful when all of the following conditions are true.
- Two classes do the same thing or similar things and have different interfaces.
- Client code could be simpler, more straightforward, and more succinct if the classes shared the same interface.
- You can’t simply alter the interface of one of the classes because it’s part of a third-party library, or it’s part of a framework that many other clients already use, or you lack source code.
The smell Alternative Classes with Different Interfaces identifies when code could be communicating with alternative classes via a common interface but for some reason does not. A simple way to solve such a problem is to rename or move methods until the interfaces are the same. If that isn’t possible, say, because you’re working with code you can’t change (like a third-party class or interface, such as a DOM Element), you may need to consider implementing an Adapter.
Refactoring to an Adapter tends to generalize code and pave the way for other refactorings to remove duplicate code. Typically in this situation you have separate client code for communicating with alternative classes. By introducing an Adapter to unify the interfaces of the alternative classes, you generalize how clients interact with those alternative classes. After that, other refactorings, such as Form Template Method, can help remove duplicated processing logic in client code. This generally results in simpler, easier-to-read client code.
- Removes or reduces duplicated code by enabling clients to communicate with alternative classes via the same interface
- Simplifies client code by making it possible to communicate with objects via a common interace
- Unifies how clients interact with alternative classes
– Complicates a design when you can change the interface of a class rather than adapting it
- A client prefers one class’s interface over another, yet the client would like to communicate with both classes via a common interface. Apply Extract Interface on the class with the client’s preferred interface to produce a common interface. Update any of this class’s methods that accept an argument of its own type to accept the argument as type common interface
- On the client class that uses the adaptee, apply Extract Class to produce a primitive adapter (a class containing an adaptee field, a getter method for the adaptee, and a setter method or constructor parameter and code for setting the adaptee’s value)
- Update all of the client class’s fields, local variables, and parameters of type adaptee to be of type adapter. This involves updating client calls on the adaptee to first obtain an adaptee reference from the adapter before invoking the adaptee method
- Wherever the client invokes the same adaptee method (via the adapter’s getter method), apply Extract Method to produce an adaptee invocation method. Parameterize this adaptee invocation method with an adaptee and make the method use the parameter value when it invokes the adaptee method
- Apply Move Method on an adaptee invocation method to move it from the client to the adapter. Every client call on the adaptee method should now go through the adapter
- Update the adapter to formally “implement” the common interface. This should be a trivial step given the work already accomplished. Change all adapter methods that accept an argument of type adapter to accept the argument as type common interface
- Update the client class so that all fields, local variables, and parameters use the common interface instead of the adapter’s type
One class adapts multiple versions of a component, library, API, or other entity.
Extract an Adapter for a single version of the component, library, API, or other entity
While software must often support multiple versions of a component, library, or API, code that handles these versions doesn’t have to be a confusing mess. Yet I routinely encounter code that attempts to handle multiple versions of something by overloading classes with version-specific state variables, constructors, and methods. Accompanying such code are comments like “This is for version X—please delete this code when we move to version Y!” Sure, like that’s ever going to happen. Most programmers won’t delete the version X code for fear that something they don’t know about still relies on it. So the comments don’t get deleted, and many versions supported by the code remain in the code.
Now consider an alternative: for each version of something you need to support, create a separate class. The class name could even include the version number of what it supports, to be really explicit about what it does. Such classes are called Adapters. Adapters implement a common interface and are responsible for functioning correctly with one (and usually only one) version of some code. Adapters make it easy for client code to swap in support for one library or API version or another. And programmers routinely rely on runtime information to configure their programs with the correct Adapter.
I refactor to Adapters fairly often. I like Adapters because they let me decide how I want to communicate with other people’s code. In a fast-changing world, Adapters help me stay insulated from highly useful but rapidly changing APIs, such as those springing eternally from the open source world.
In some cases, Adapters may adapt too much. For example, a client needs access to behavior on an adaptee, yet it cannot access that behavior because it only has access to the adaptee via an Adapter. In that case, the Adapter must be redesigned to accommodate client needs.
- Isolates differences in versions of a component, library, or API
- Makes classes responsible for adapting only one version of something
- Provides insulation from frequently changing code
– Can shield a client from important behavior that isn’t available on the Adapter
There are different ways to go about this refactoring, depending on how your code looks before you begin. For example, if you have a class that uses a lot of conditional logic to handle multiple versions of something, it’s likely that you can create Adapters for each version by repeatedly applying Replace Conditional with Polymorphism. If you have a case like the one shown in the code sketch—in which an existing Adapter class supports multiple versions of a library with version-specific variables and methods—you’ll extract multiple Adapters using a different approach. Here I outline the mechanics for this latter scenario.
- Identify an overburdened adapter, a class that adapts too many versions of something
- Create a new adaper, a class produced by applying Extract Subclass or Extract Class for a single version of the multiple versions supported by the overburdened adapter. Copy or move all instance variables and methods used exclusively for that version into the new adapter
- Repeat step 2 until the overburdened adapter has no more version-specific code
- Remove any duplication found in the new adapters by applying refactorings like Pull Up Method and Form Template Method
Numerous methods on a class combine elements of an implicit language.
Define classes for elements of the implicit language so that instances may be combined to form interpretable expressions
An Interpreter is useful for interpreting simple languages. A simple language is one that has a grammar that may be modeled using a small number of classes. Sentences and expressions in simple languages are formed by combining instances of the grammar’s classes, typically using a Composite structure.
Programmers divide into two camps with respect to the Interpreter pattern: those who are comfortable implementing it and those who aren’t. However, whether or not you’re comfortable with terms like parse trees and abstract syntax trees, terminal and nonterminal expressions, implementing an Interpreter is only slightly more complicated than implementing a Composite. The trick is knowing when you need an Interpreter.
Interpreters are often used within systems to allow for the runtime configuration of behavior. For example, a system may accept a user’s query preferences through a user interface and then dynamically produce an interpretable object structure that represents the query. In this way, Interpreters can provide a level of power and flexibility that isn’t possible when all behavior in a system is static and can’t be configured dynamically.
- Supports combinations of language elements better than an implicit language does
- Requires no new code to support new combinations of language elements
- Allows for runtime configuration of behavior
– Has a start-up cost for defining a grammar and changing client code to use it
– Requires too much programming when your language is complex
– Complicates a design when a language is simple
These mechanics are heavily weighted towards the use of Interpreter in the context of the Specification and Query Object patterns because most of the Interpreter implementations I’ve written or encountered have been implementations of those two patterns. In this context, an implicit language is modeled using numerous object selection methods, each of which iterates across a collection to select a specific set of objects.
- Find an object selection method that relies on a single criterion argument (e.g., double targetPrice) to find a set of objects. Create a concrete specification class for the criterion argument, which accepts the argument’s value in a constructor and provides a getter method for it. Within the object selection method, declare and instantiate a variable of type concrete specification and update the code so access to the criterion is obtained via the concrete specification’s getter method
- Apply Extract Method on the conditional statement in the object selection method to produce a method called
isSatisfiedBy(), which should have a Boolean result. Now apply Move Method to move this method to the concrete specification
- Repeat steps 1 and 2 for similar object selection methods, including methods that rely on the criteria for object selection
- If you have an object selection method that relies on multiple concrete specifications (i.e., the method now instantiates more than one concrete specification for use in its object selection logic), apply a modified version of step 1 by creating a composite specification (a class composed of the concrete specifications instantiated inside the object selection method). You may pass the concrete specifications to the composite specification via its constructor or, if there are many concrete specifications, supply an
add(…)method on the composite specification
- Each object selection method now works with one specification object (i.e., one concrete specification or one composite specification). In addition, the object selection methods are identical except for specification creation code. Remove duplicated code in the object selection methods by applying Extract Method on the identical code from any object selection method. Name the extracted method something like
selectBy(…)and have it accept one argument of type specification interface and return a collection of objects (e.g.,
public List selectBy(Spec spec))
- Apply Inline Method on every object selection method.
A refactoring that improves the protection of existing code must do so in a way that doesn’t alter the behavior of the existing code. All three refactorings in this section do just that. Your motivation for applying them may be to improve protection or it may be a standard refactoring motivation, such as to reduce duplication or to simplify or clarify code.
Replace Type Code with Class helps protect a field from assignments to incorrect or unsafe values. This is particularly important when a field controls what behavior gets executed at runtime because an incorrect assignment could put an object into an invalid state. Replace Type Code with Class uses a class rather than an enum to constrain what values may be assigned to a field. Does an enum provide a better way to implement this refactoring or even render this refactoring out of date? It does not. The main difference between a class and an enum is that you can add behavior to a class. This is important because the class produced during Replace Type Code with Class may need to be extended with behavior as you apply a sequence of refactorings. This is exactly what occurs during the refactoring Replace State-Altering Conditionals with State.
Limit Instantiation with Singleton is useful when you want to control how many instances of a class can be instantiated. Typical motivations for applying this refactoring are to reduce memory usage or improve performance. A poor motivation for refactoring to a Singleton is to give a piece of code access to hard-to-reach information (see Inline Singleton, for a discussion of this). In general, it’s best to apply Limit Instantiation with Singleton only when a profiler informs you that doing so is worthwhile.
Introduce Null Object is a refactoring that helps transform how code is protected from null values. If you have a lot of the same conditional logic that checks for the same null value, you can likely simplify and condense the code by refactoring it to use a Null Object.
A field’s type (e.g., a
int) fails to protect it from unsafe assignments and invalid equality comparisons.
Constrain the assignments and equality comparisons by making the type of the field a class
A primary motivation for refactoring from a type code to a class is to make code type-safe. One way to do that is to constrain the possible values that may be assigned to or equated with a field or variable. Consider the following type-unsafe code:
This code creates a
SystemPermission object. The constructor for this object sets its state field equal to the
Other methods within
SystemPermission assign state to system permission states such as
DENIED. Given that each of these state types was defined using
String constants (like
public final static String REQUESTED = "REQUESTED") and state was defined as type
String, the two assert statements above both pass because the state field, which is accessed via the call to
permission.state(), is considered equal to both
SystemPermission.REQUESTED and the
What’s the problem with this? The problem is that using a
String value in this context is error prone. For example, what if an assert statement were accidently written like this:
- Provides better protection from invalid assignments and comparisons
– Requires more code than using unsafe type does
- Identify a type-unsafe field, a field declared as a primitive or non-class-based type that is assigned to or compared against a family of type-unsafe constants. Self-encapsulate the type-unsafe field by applying Self Encapsulate Field
- Create a new class, a concrete class that will soon replace the type used for the type-unsafe field. Name this class after the kinds of types it will store. For the moment, don’t provide any constructor for the new class
- Choose a constant value that the type-unsafe field is assigned to and/or compared against and define a new version of this constant in your new class by creating a constant that is an instance of the new class. In Java, it’s common to declare this constant to be public final static
- In the class that declared the type-unsafe field, create a type-safe field, a field whose type is the new class. Create a setting method for it
- Wherever an assignment is made to the type-unsafe field, add a similar assignment statement to the type-safe field, using the appropriate constant in the new class
- Change the getter method for the type-unsafe field so that its return value is obtained from the type-safe field. This requires making the constants in the new class capable of returning the correct value
- In the class that declared the type-unsafe field, delete the type-unsafe field, the setter method for it, and all calls to the setting method
- Find all references to the type-unsafe constants and replace them with calls to the corresponding constant in the new class. As part of this step, change the getter method for the type-unsafe field so its return type is the new class and make all changes necessary to callers of the revised getter method
Your code creates multiple instances of an object, and that uses too much memory or slows system performance.
Replace the multiple instances with a Singleton
If you want to be a good software designer, don’t optimize code prematurely. Prematurely optimized code is harder to refactor than code that hasn’t been optimized. In general, you’ll discover more alternatives for improving your code before it has been optimized than after.
On the other hand, sometimes it’s a good decision to refactor to a Singleton, as in the following scenario.
- Users of your system are complaining about system performance
- Your profiler tells you that you can improve performance by not instantiating certain objects over and over again
- The objects you want to share have no state or contain state that is sharable
- Improves performance
– Is easily accessible from anywhere. In many cases, this may indicate a design flaw
– Is not useful when an object has state that can’t be shared
Before you perform this refactoring, make sure the object you want to turn into a Singleton has no state or has state that is sharable. Because most classes that end up becoming Singletons have one constructor, these mechanics assume you have one constructor on your class.
- Identify a multiple instance class, a class that gets instantiated more than once by one or more clients. Apply the mechanics from Replace Constructors with Creation Methods even though your class has only one constructor. The return type for your new Creation Method should be the multiple instance class
- Declare a singleton field, a private static field of type multiple instance class in the multiple instance class and, if possible, initialize it to an instance of the multiple instance class
- Make your Creation Method return the value in the singleton field. If it must be lazily instantiated, do that lazy instantiation in the Creation Method, based on whatever parameters are passed in
Logic for dealing with a null field or variable is duplicated throughout your code.
Replace the null logic with a Null Object, an object that provides the appropriate null behavior
If a client calls a method on a field or variable that is null, an exception may be raised, a system may crash, or similar problems may occur. To protect our systems from such unwanted behavior, we write checks to prevent null fields or variables from being called and, if necessary, specify alternative behavior to execute when nulls are encountered:
Repeating such null logic in one or two places in a system isn’t a problem, but repeating it in multiple places bloats a system with unnecessary code. Compared with code that is free of null logic, code that is full of it generally takes longer to comprehend and requires more thinking about how to extend. Null logic also fails to provide null protection for new code. So if new code is written and programmers forget to include null logic for it, null errors can begin to occur.
The Null Object pattern provides a solution to such problems. It removes the need to check whether a field or variable is null by making it possible to always call the field or variable safely. The trick is to assign the field or variable to the right object at the right time. When a field or variable can be null, you can make it refer to an instance of a Null Object, which provides do-nothing, default, or harmless behavior. Later, the field or variable can be assigned to something other than a Null Object. Until that happens, all invocations safely route through the Null Object.
- Prevents null errors without duplicating null logic
- Simplifies code by minimizing null tests
– Complicates a design when a system needs few null tests
– Can yield redundant null tests if programmers are unaware of a Null Object implementation
– Complicates maintenance. Null Objects that have a superclass must override all newly inherited public methods
These mechanics assume you have the same null logic scattered throughout your code because a field or local variable may be referenced when it is still null. If your null logic exists for any other reason, consider applying Martin Fowler’s mechanics for Introduce Null Object. The term source class in the following steps refers to the class that you’d like to protect from nulls.
- Create a null object by applying Extract Subclass on the source class or by making your new class implement the interface implemented by the source class. If you decide to make your null object implement an interface, but that interface doesn’t yet exist, create it by applying Extract Interface on the source class
- Look for a null check (client code that invokes a method on an instance of the source class if it is not null, or performs alternative behavior if it is null). Overide the invoked method in the null object so it implements the alternative behavior
- Repeat step 2 for other null checks associated with the source class
- Find a class that contains one or more occurrences of the null check and initialize the field or local variable that is referenced in the null check to an instance of the null object. Perform this initialization at the earliest possible time during the lifetime of an instance of the class (e.g., upon instantiation)
- In the class you selected in step 4, remove every occurrence of the null check
- Repeat steps 4 and 5 for every class with one or more occurrences of the null check
A good deal of code in software systems accumulates information. The refactorings in this section target the improvement of code that accumulates information within an object or across several objects.
A Collecting Parameter is an object that visits methods in order to accumulate information from them. The visited methods may reside within one object or many objects. Each visited method provides the Collecting Parameter with information. When all relevant methods have been visited, the accumulated information can be obtained from the Collecting Parameter.
Move Accumulation to Collecting Parameter is often used in conjunction with the refactoring Compose Method. The combination of these refactorings applies best when you have a long method that has many lines of code for accumulating information. To break the method into smaller parts, each of which handles a piece of the accumulation, you extract methods that accept and write to a Collecting Parameter.
The Collecting Parameter pattern resembles a Visitor in its ability to accumulate information from several objects. It differs from a Visitor in how it does the accumulation. Whereas visited objects pass themselves to a Visitor instance, objects that are visited by a Collecting Parameter simply call methods on the Collecting Parameter to provide it with information. If you have a lot of diverse information to accumulate from heterogeneous objects (i.e., objects with different interfaces), a Visitor will likely provide a cleaner design than a Collecting Parameter. That said, I frequently encounter code that could benefit from Move Accumulation to Collecting Parameter, while I infrequently encounter code that needs Move Accumulation to Visitor.
Unlike a Collecting Parameter, a Visitor is only useful for accumulating information from many objects, not one. It is also more applicable when you’re accumulating information from heterogeneous objects, not homogeneous ones (i.e., objects that share the same interface). Because a Visitor is harder to implement than a Collecting Parameter, it’s better to consider a Collecting Parameter solution before considering a Visitor one.
While the Visitor pattern is useful for certain types of accumulation, it is by no means limited to accumulation in what it can do. For example, a Visitor can visit an object structure and add objects to that structure during its journey. I use Visitors infrequently even for information accumulation, so I did not write about other refactorings to Visitor; they too are rare.
You have a single bulky method that accumulates information to a local variable.
Accumulate results to a Collecting Parameter that gets passed to extracted methods
Kent Beck defined the Collecting Parameter pattern in his classic book, Smalltalk Best Practice Patterns. A Collecting Parameter is an object that you pass to methods in order to collect information from those methods. This pattern is often coupled with Composed Method.
To decompose a bulky method into a Composed Method, you often need to decide how to accumulate information from the methods called by the Composed Method. Instead of having each of the methods return a result, which you later combine into a final result, you can incrementally accumulate a result by passing a Collecting Parameter to each of the methods. The methods write their information to the Collecting Parameter, which accumulates all of the results.
A Collecting Parameter may also be passed to methods on multiple objects. When it visits multiple objects, a Collecting Parameter accumulates information in one of two ways. Either each object calls back on a method or methods on the Collecting Parameter to pass data to it, or the objects pass themselves to the Collecting Parameter, which then calls back on the objects to obtain their data.
- Helps transform bulky methods into smaller, simpler, z methods
- Can make resulting code run faster
- Identify an accumulation method, a method that accumulates information into a result. The result, a local variable, will become a Collecting Parameter. If the result’s type won’t let you iteratively gather data across methods, change its type. For example, Java’s String won’t let you accumulate results across methods, so use a
StringBuffer(see the Example section for more on this)
- In the accumulation method, find an information accumulation step and apply Extract Method to extract it into a private method. Make sure the method’s return type is void, and pass the result to it as a parameter. Inside the extracted method, write information to the result
- Repeat step 2 for every accumulation step, until the original code has been replaced with calls to extracted methods that accept and write to the result. The accumulation method should now contain three lines of code that
- Instantiate a result
- Pass a result to the first of many methods
- Obtain a result’s collected information
A method accumulates information from heterogeneous classes.
Move the accumulation task to a Visitor that can visit each class to accumulate the information
Ralph Johnson, one of the four authors of Design Patterns, once observed, “Most of the time you don’t need Visitor, but when you do need Visitor, you really need Visitor!” So when do you really need Visitor? Let’s review what Visitors are before answering that question.
A Visitor is a class that performs an operation on an object structure. The classes that a Visitor visits are heterogeneous, which means they hold unique information and provide a specific interface to that information. Visitors can easily interact with heterogeneous classes by means of double-dispatch. This means that each of a set of classes accepts a Visitor instance as a parameter (via an “accept” method:
accept(Visitor visitor)) and then calls back on the Visitor, passing itself to its corresponding visit method, as shown in the following diagram.
Now let’s get back to the question: When do you really need a Visitor? In general, you need a Visitor when you have numerous algorithms to run on the same heterogeneous object structure and no other solution is as simple or succinct as a Visitor. For example, say you have three domain classes, none of which share a common superclass and all of which feature code for producing different XML representations.
Another case when a Visitor is needed is when you have numerous external accumulation methods. Such methods typically use an Iterator and resort to type-casting heterogeneous objects to access specific information:
Finally, there are times when you have neither an external nor an internal accumulation method, yet your design could be improved by replacing your existing code with a Visitor. On the HTML Parser project, we once accomplished an information accumulation step by writing two new subclasses as shown in the figure on the following page.
The double-edged sword of the Visitor pattern is its power and sophistication. When you need a Visitor, you really need one, as Ralph says. Unfortunately, too many programmers feel the need to use Visitor for the wrong reasons, like showing off or because they’re still “patterns happy.” Always consider simpler solutions before refactoring to Visitor, and use this pattern most judiciously.
- Accommodates numerous algorithms for the same heterogeneous object structure
- Visits classes in the same or different hierarchies
- Calls type-specific methods on heterogeneous classes without type-casting
– Complicates a design when a common interface can make heterogeneous classes homogeneous
– A new visitable class requires a new accept method along with a new visit method on each Visitor
– May break encapsulation of visited classes
An accumulation method gathers information from heterogeneous classes. An external accumulation method exists on a class that isn’t one of the heterogeneous classes, while an internal accumulation method exists on the heterogeneous classes themselves. In this section you will find mechanics for both internal and external accumulation methods. In addition, I’ve provided a third set of mechanics for Visitor replacement, which you can use if you have neither an internal nor an external accumulation method yet can achieve a better design by rewriting your accumulation code as a Visitor.
The class that contains your accumulation method is known in this refactoring as the host. Does it make sense for your host to play the role of Visitor? If your host is already playing too many roles, extract the accumulation method into a new host by performing Replace Method with Method Object prior to this refactoring.
- In the accumulation method, find any local variables that are referenced in multiple places by the accumulation logic. Convert these local variables to fields of the host class
- Apply Extract Method on the accumulation logic for a given accumulation source, a class from which information is accumulated. Adjust the extracted method so it accepts an argument of the accumulation source’s type. Name the extracted method
- Apply Extract Method on the body of an
accept(…)method to produce a method called
ClassNameis the name of the accumulation source associated with the
accept(…)method. The new method will accept one argument of the accumulation source’s type (e.g.,
- Apply Move Method to move every
accept(…)method to its corresponding accumulation source. Each
accept(…)method will now accept an argument of the host’s type
- In the accumulation method, apply Inline Method on every call to an
- Apply Unify Interfaces on the superclasses and/or interfaces of the accumulation sources so the
accept(…)method may be called polymorphically
- Generalize the accumulation method to call the
accept(…)method polymorphically for every accumulation source
- Apply Extract Interface on the host to produce a visitor interface, an interface that declares the visit methods implemented by the host
- Change the signature on every occurrence of the
accept(…)method so it uses the visitor interface
Use these mechanics when your accumulation method is implemented by the heterogeneous classes from which information is gathered. These mechanics assume that the heterogeneous classes are part of a hierarchy because that is a common case. The steps for these mechanics are largely based on mechanics defined in the paper “A Refactoring Tool for Smalltalk”.
- Create a visitor by creating a new class. Consider using
visitorin the class name
- Identify a visitee, a class from which the visitor will accumulate data. Add a method to the visitor called
visitClassName(…), where ClassName is the name of the visitee (e.g.,
visitor.visitEndTag(…)). Make the visit method’s return type void, and make it take a visitee argument (e.g.,
public void visitStringNode(StringNode stringNode))
- On every visitee, apply Extract Method on the body of the accumulation method so it calls a new method, which will be called the accept method. Make the signature of the accept method identical in all classes, so every accumulation method contains the same code for calling its accept method
- The accumulation method is now identical in every class. Apply Pull Up Method to move it to the hierarchy’s superclass
- Apply Add Parameter to add an argument of type visitor to every implementation of the accept method. Make the accumulation method pass in a new instance of the visitor when it calls the accept method
- Produce a visit method on the visitor by applying Move Method on a visitee’s accept method. The accept method now calls a visit method that accepts an argument of type visitee
For example, given a visitee called
StringNode and a visitor called
Visitor, we’d have the following code:
This refactoring assumes you have neither an internal nor an external accumulation method, yet your code would be better if it were replaced with a Visitor.
- Create a concrete visitor by creating a new class. Consider using
visitorin the class name
- Identify a visitee, a class from which the concrete visitor must accumulate data. Add a method to the concrete visitor called visitClassName, where ClassName is the name of the visitee (e.g.,
concreteVisitor.visitEndTag(…)). Make the visit method’s return type void and make it take a visitee argument (e.g.,
public void visitStringNode(StringNode stringNode)).
- Add to the same visitee (from step 2) a public accept method that takes as a parameter the concrete visitor or, if you have one, the abstract visitor. Make the body of this method call back on the concrete visitor’s visit method, passing a reference to the visitee
- Repeat steps 2 and 3 for every visitee. You now have the skeleton of your concrete visitor
- Implement a public method on your concrete visitor to obtain its accumulated result. Make the accumulated result be empty or null
- In the accumulation method, define a local field for the concrete visitor and instantiate it. Next, find accumulation method code where information is accumulated from each visitee, and add code to call each visitee’s accept method, passing in the concrete visitor instance. When you’re done, update the accumulation method so it uses the concrete visitor’s accumulated result instead of its normal result. This last part will cause your tests to break
- Implement the method bodies for each visit method on the concrete visitor. This step is big, and there’s no single set of mechanics that will work for it because all cases vary. As you copy code from the accumulation method into each visit method, make it fit into its new home by
- Ensuring each visit method can access essential data/logic from its visitee
- Declaring and initializing concrete visitor fields that are accessed by two or more of the visit methods
- Passing essential data (used in accumulation) from the accumulation method to the concrete visitor’s constructor (e.g., a
TagAccumulatingVisitoraccumulates all Tag instances that match the string,
tagNameToFind, which is a value supplied via a constructor argument)
- Remove as much old code from the accumulation method as possible
- You should now be left with code that iterates over a collection of objects, passing the concrete visitor to the accept method for each visitee. If some of the objects being iterated over don’t have an accept method (i.e., aren’t visitees), define a do-nothing accept method on those classes (or on their base class), so your iteration code doesn’t have to distinguish between objects when it calls the accept method
- Create a local accept method by applying Extract Method on the accumulation method’s iteration code. This new method should take the concrete visitor as its sole argument and should iterate over a collection of objects, passing the concrete visitor to each object’s accept method
- Move the local accept method to a place where it will more naturally fit, such as a class that other clients can easily access
The refactorings in this section are low-level transformations used by the higher-level refactorings in the catalog. These refactorings fit well with the refactorings in Refactoring.
Chain Constructors is about removing duplication in constructors by having them call each other. This refactoring is used by the refactoring Replace Constructors with Creation Methods.
Unify Interfaces is useful when you need a superclass and/or interface to share the same interface as a subclass. The usual motivation for applying this refactoring is to make it possible to treat objects polymorphically. The refactorings Move Embellishment to Decorator and Move Accumulation to Visitor both make use of this refactoring.
Extract Parameter is useful when a field is assigned to a locally instantiated value and you’d rather have that value supplied by a parameter. While this can be useful in many situations, the refactoring Move Embellishment to Decorator uses this refactoring just after the application of Replace Inheritance with Delegation.
You have multiple constructors that contain duplicate code.
Chain the constructors together to obtain the least amount of duplicate code
Code that’s duplicated across two or more of a class’s constructors is an invitation for trouble. If someone adds a new variable to a class and updates a constructor to initialize the variable, but then neglects to update the other constructors—bang, say hello to your next defect. The more constructors you have in a class, the more duplication will hurt you. It is therefore a good idea to reduce or remove duplication in your constructors.
We often accomplish this refactoring with constructor chaining: specific constructors call more general-purpose constructors until a final constructor is reached. If you have one constructor at the end of every chain, it is a catch-all constructor because it handles every constructor call. A catch-all constructor often accepts more parameters than other constructors.
If you find that having many constructors on your class detracts from its usability, consider applying Replace Constructors with Creation Methods.
- Find two constructors that contain duplicate code. Determine whether one can call the other such that duplicate code can be safely (and, hopefully, easily) deleted from one of these constructors. Then make the one constructor call the other constructor such that duplicate code is reduced or eliminated
- Repeat step 1 for all constructors in the class, including ones you’ve already touched, in order to obtain as little duplication across all constructors as possible
- Change the visibility of any constructors that may not need to be public
You need a superclass and/or interface to have the same interface as a subclass.
Find all public methods on the subclass that are missing on the superclass/interface. Add copies of these missing methods to the superclass, altering each one to perform null behavior
To process objects polymorphically, the classes of the objects need to share a common interface, whether a superclass or an actual interface. This refactoring addresses the case when a superclass or interface needs to have the same interface as a subclass.
I came across the need for this refactoring on two separate occasions. Once when I was applying Move Embellishment to Decorator, an emerging Decorator needed the same interface as a subclass. The easiest way to make that happen was to apply Unify Interfaces. Similarly, during an application of the refactoring Move Accumulation to Visitor, duplicate code could be removed if certain objects shared the same interface, which Unify Interfaces made possible.
After applying this refactoring on a superclass and subclass, I sometimes apply Extract Interface on the superclass to produce a stand-alone interface. I usually do this when an abstract base class contains state fields and I don’t want implementors of the common base class, such as a Decorator, to inherit those fields. See Move Embellishment to Decorator for an example.
Unify Interfaces is often a temporary step on the way to somewhere else. For example, after you perform this refactoring, you may perform a sequence of refactorings that allows you to remove methods you added while unifying your interfaces. Other times, a default implementation of a method on an abstract base class may no longer be needed after applying Extract Interface.
Find a missing method, a public method on the subclass that isn’t declared on the superclass and/or interface.
- Add a copy of the missing method to the superclass/interface. If you’re adding the missing method to a superclass, modify its body to perform null behavior
- Repeat until the superclass/interface and subclass share the same interface
A method or constructor assigns a field to a locally instantiated value.
Assign the field to a parameter supplied by a client by extracting one-half of the assignment statement to a parameter
Sometimes you want to assign a field inside an object to a value provided by another object. If the field is already assigned to a local value, you can extract one-half of the assignment statement to a parameter so that a client can supply the field’s value rather than the host object.
I needed this refactoring after performing Replace Inheritance with Delegation. At the end of that refactoring, a delegating class contains a field for an object it delegates to (the delegatee). The delegating class assigns this delegate field to a new instance of the delegate. Yet I needed a client object to supply the delegate’s value. Extract Parameter allowed me to simply extract the delegate instantiation code to a parameter value supplied by a client.
- The assignment statement for the field must be in a constructor or method before you can do this refactoring. If it isn’t already in a constructor or method, move it to one
- Apply Add Parameter to pass in the value for the field, using the field’s type as the type for the parameter. Make the parameter’s value be the value the field is assigned to within its host object. Alter the assignment statement so the field is assigned to the new parameter
- When you have finished this refactoring, you may wish to remove unused parameters by applying Remove Parameter.