The Refactorings
Words and concepts allow us to reason with things. We create vocabulary and in doing so we can think with more complexity, accomplish more, and have greater understanding. When you don't have vocabulary and concepts for things you really are more limited (<-- crazy story if you want your mind blown). I propose Fowler's Refactorings are sort of like that. Or I'm going to think of them so. They're like the low level concept that help you get to the greater concepts (being, perhaps, your domain language or other patterns, etc), which help get you to that euphoric bliss of writing software where you are never confused, never held back by any complicated or sticky code. Ok perhaps impossible, but I do believe these help propel you... somewhere further along.
So I'm going to share Fowler's catalogue of refactorings. Many of us make use of a lot of these on a regular basis, and some may seem a bit rudimentary. In whole, however, I do think these can help give more vocabulary to us, and therefore more options, when dealing with the software we're developing. Many of them are so simple they make refactoring seem... easy. And perhaps it is (at least more than I know I've often made it in years past). Often I've thought of refactoring in terms of a giant mess that would take a week to fix. I've been more inspired though, as Fowler provokes you, to think of refactoring as something simple, easy, something that you can do on an ongoing basis that doesn't have to be big and daunting (more on that later).
So here's a brief overview of each section of refactorings (I've listed and linked each of the refactorings to examples on Fowler's site):
Composing Methods
Fowler says one of the most common problems and keys to well maintained software is simple methods. This first section of his refactorings deals with this: managing the internals of methods so they are easier to understand. One of the most common ones here is Extract Method (extracting a piece of logic from a method into a new, well named, method). Or, if an extracted method is just as clear as it's method name you can do the opposite: Inline Method. As with all of the refactorings, one of the goals is clarity and the ability to understand, whether that be delegating to a new object or method, removing unnecessary delegation (if it's not pulling it's weight) or simply creating instance variables to explain the purpose of something.
Extract Method, Inline Method, Inline Temp, Replace Temp with Query, Introduce Explaining Variable, Split Temporary Variable, Remove Assignments to Parameters, Replace Method with Method Object, Substitute Algorithm, Extract Surrounding Method, Introduce Class Annotation, Introduce Named Parameter, Remove Named Parameter, Dynamic Method Definition, Replace Dynamic Receptor With Dynamic Method Definition, Isolate Dynamic Receptor, Move Eval From Runtime To Parse Time, Remove Unused Default Parameter, Replace Loop With Collection Closure Method
Moving Features Between Objects
One of the (if not the) most fundamental decisions on OO, Fowler says, is deciding where to put responsibilities. In his typical and honest fashion, he repeats throughout the book that he never gets this right the first time, but he's found it less of a burden knowing that he can always refactor. This section is all about organization between objects or even adding functionality to external objects when you can't modify them. Reading these helped emphasized that point: you aren't going to know right up front where things should live, and it doesn't have to be a burden to move things around. Add delegation for clarity, or remove it if it's no help. You can slowly and easily mold your objects to help them give you the most benefit and clarity.
Move Method, Move Field, Extract Class, Inline Class, Hide Delegate, Remove Middle Man, Introduce Foreign Method, Introduce Local Extension
Organizing Data
These refactorings are all about making data easier to work with, whether it be replacing mysterious numbers with a named constant, creating classes for simple but confusing data types like arrays (don't you love reading things like, person[0][1] = "John", person[0][2]="Doe"?), or encapsulating data and giving an external and safe interface for it. A helpful solidification for me in reading this section: The way you work with data and the external interface you give objects not only gives your objects protection (or lack thereof), but it can really help make things easier to reason with. Privatizing methods and attributes that are only used internally not only helps keep things safe, but also solidifies what the object's main purpose is and how you want others (or your future self) to understand it. Replacing data field public accessors with add/remove methods instead not only helps protect you from accidentally messing up or deleting data by replacing the entire (for example) array but it helps make reading your code easier. I recently saw the confusing affects of this (an unnecessary public setter): I'll occasionally glance at this setter and wrack my brain for a few minutes trying to figure out why it would be there, then moving on because it's not something I'm concerned with at the moment. I realized it actually had absolutely no use, and was really causing only confusion.
Self Encapsulate Field, Replace Data Value with Object, Change Value to Reference, Change Reference to Value, Replace Array with Object, Duplicate Observed Data, Change Unidirectional Association to Bidirectional, Change Bidirectional Association to Unidirectional, Replace Magic Number with Symbolic Constant, Encapsulate Field, Encapsulate Collection, Replace Record with Data Class, Replace Type Code with Class, Replace Type Code with Subclasses, Replace Type Code with State/Strategy, Replace Subclass with Fields, Eagerly Initialized Attribute, Lazily Initialized Attribute, Replace Hash With Object, Replace Subclass With Fields, Replace Type Code With Polymorphism
Simplifying Conditional Expressions
Everyone I'm sure has seen a pretty large and nasty conditional trees. This especially gets confusing when you not only find many if/else branches but the conditionals themselves are a mess of data that is hard to make sense of (Decompose Conditional---simply creating a readable method for those---is an easy answer to that.. a more specific version of Extract Method). This section gives some good principles and tips to help shorten and make clearer your conditionals. I particularly liked his idea of grouping and returning edge cases early Replace Nested Conditional with Guard Clauses, and making more use of breaks (Remove Control Flag).
Decompose Conditional, Consolidate Conditional Expression, Consolidate Duplicate Conditional Fragments, Remove Control Flag, Replace Nested Conditional with Guard Clauses, Replace Conditional with Polymorphism, Introduce Null Object, Introduce Assertion, Recompose Conditional
Making Method Calls Simpler
Deciding where things belong one thing, but deciding how to interact with it is another. This section deals with making interfaces more simple. Many of these have to do with parameters. One quick principle from this that I enjoyed is to strive to keep methods that have side affects separate from ones that return data (Separate Query from Modifier). Another useful one was Introduce Parameter Object where you create an object for multiple parameters, especially useful when you often see the same parameters passed around. As he advocates, more small methods are better than fewer larger methods. This can be done badly though, with endless and confusing delegation. But the key to avoid this is good naming. Renaming is one of the simplest and most important things you can do (and again, he notes, you won't get this right the first time).
Rename Method, Add Parameter, Remove Parameter, Separate Query from Modifier, Parameterize Method, Replace Parameter with Explicit Methods, Preserve Whole Object, Replace Parameter with Method, Introduce Parameter Object, Remove Setting Method, Hide Method, Replace Constructor with Factory Method, Encapsulate Downcast, Replace Error Code with Exception, Replace Exception with Test, Introduce Expression Builder, Introduce Gateway
Dealing With Generalizations
This section deals with moving things around within an objects hierarchy/inheritance. Extract Interface and Replace Inheritance with Delegation are a couple of the more interesting and useful ones I found here.
Pull Up Field, Pull Up Method, Pull Up Constructor Body, Push Down Method, Push Down Field, Extract Subclass, Extract Superclass, Extract Interface, Collapse Hierarchy, Form Template Method, Replace Inheritance with Delegation, Replace Delegation with Inheritance, Extract Module, Inline Module, Replace Abstract Superclass With Module, Replace Delegation With Hierarchy
Is it really that easy?
Kent Beck at the end notes that refactoring is not always as simple as this (they even dedicate a chapter to some common larger refactorings). He has seen refactorings done over months, or even years when things are more messy or complex. Often time there is a transition between where you are and where you want to be, and this can make things complicated, especially when different team members have different ideas of where things should be going. His solution to this is to be adamant about continual improvement, as well as getting everyone oriented around the same goal and aware that things are in transition. You may have an idea of where you want your code to be, and it may seem like a gigantic and daunting rats nest to drudge through before you get there. But there are a lot of small steps you can take now that you'll start seeing immediate benefits from. I've been inspired, since reading this, to be more mindful of ways I can continually do this, and to see refactoring as an easy and safe way to really mold objects into what you want---the design or patterns you are looking for--and to get to, or even explore, where you want to go.
blog comments powered by Disqus