Refactoring - Simplifying Conditional Expressions

1. Decompose Condtional

You have a complicated conditional (if-then-else) statement. Extract methods from the condition, “then” part, and “else” parts. Mechanics *1. Extract the condition into its own method. *2. Extract the “then” part and the “else” part into their own methods.

2. Recompose Conditional

You have conditional code that is unnecessarily verbose and does not use the most readable Ruby construct. Replace the conditional code with the more idiomatic Ruby construct.

before:
parameters = params ? params : []

after:
parameters = params || []

3. Consolidate Conditional Expression

You have a sequence of conditional tests with the same result. Combine them into a single conditional expression and extract it.

4. Consolidate Duplicate Conditional Fragments

The same fragment of code is in all branches of a conditional expression. Move it outside the expression.

5. Replace Condtional with Polymorphism

Motivation One of the grandest sounding words in object jargon is polymorphism. The essence of polymorphsim is that it allows you to avoid writing an explicit con- ditional when you have objects whose behavior varies depending on their types. As a result you find that case statements or if-then-else statements that switch on type codes are much less common in an Object-Oriented program.

Polymorphism gives you many advantages. The biggest gain occurs when this same set of conditions appears in many places in the program. If you want to add a new type, you have to find and update all the conditionals. But with polymorphism you just create a new class and provide the appropriate methods. Clients of the class don’t need to know about the polymorphism, which reduces the dependencies in your system and makes it easier to update.

Ruby’s duck typing makes it easy to introduce polymorphism. In a statically typed language like Java, you need inheritance or implementation of an inter- face to be able to call a method polymorphically. But in Ruby, an object’s cur- rent set of methods—not its inheritance from a particular class—determines its valid semantics. So long as objects A and B have the same method, you can call them in the same way.

Mechanics

Polymorphism in Ruby can be achieved in a couple of ways. In its simplest form, you can implement the same method signature on multiple objects and call these methods polymorphically. You can introduce a module hierarchy and have the method that is to be called polymorphically on the module. Or you can introduce an inheritance hierarchy and have the method that is to be called polymorphically on the subclasses. In each case, the mechanics are the same. The code you target may be a case statement or an if statement.

*1. If the conditional statement is one part of a larger method, take apart the conditional statement and use Extract Method. *2. If necessary use Move Method to place the conditional at the appropriate place in the object structure. **3. Pick one of the polymorphic objects. Create a method on the polymor- phic object that will override the conditional statement method. Copy the body of that leg of the conditional statement into the polymorphic method and adjust it to fit.

6. Introuduce a Null Object

You have repeated checks for a nil value. Replace the nil value with a null object.

The essence of polymorphism is that instead of asking an object what type it is and then invoking some behavior based on the answer, you just invoke the behavior. The object, depending on its type, does the right thing. One of the less intuitive places to do this is where you have a null value in a field.

Refactoring - Simple Method Calls

1.. Rename Method

The name of a method does not reveal its purpose. Change the name of the method.

2. Add Parameter

A method needs more information from its caller. Add a parameter for an object that can pass on this information.

3. Remove Parameter

A parameter is no longer used by the method body. Remove it.

4. Seperate Query from Modifier

You have a method that returns a value and also changes the state of an object. Create two methods, one for the query and one for the modification.

4. Parameterize Method

You have a method that returns a value and also changes the state of an object. Create two methods, one for the query and one for the modification.

Motivation You may see a couple of methods that do similar things but vary depending on a few values. In this case you can simplify matters by replacing the separate methods with a single method that handles the variations by parameters. Such a change removes duplicate code and increases flexibility, because you can deal with other variations by adding parameters.

5. Perserve Whole Object

You are getting several values from an object and passing these values as param- eters in a method call. Send the whole object instead.

6. Replace Parameter with Method

An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method. Remove the parameter and let the receiver invoke the method.

7. Introduce Parameter Object

You have a group of parameters that naturally go together. Replace them with an object.

8. Remove Setting Method

A field should be set at creation time and never altered. Remove any setting method for that field. Providing a setting method indicates that a field may be changed. If you don’t want that field to change once the object is created, don’t provide a setting method. That way your intention is clear and you often remove the possibility that the field will change.

9. Hide Method

A method is not used by any other class. Make the method private.

10. Replace Constructor with Factory Method

You want to do more than simple construction when you create an object. Replace the constructor with a factory method. The most obvious motivation for Replace Constructor with Factory Method is when you have conditional logic to determine the kind of object to create. If you need to do this conditional logic in more than one place, it’s time for a Factory Method.

11.Replace Error Code with Exception

A method returns a special code to indicate an error. Raise an exception instead.

12. Replace Exception with Test

You are raising an exception on a condition the caller could have checked first. Change the caller to make the test first. Exceptions are an important advance in programming languages. They allow us to avoid complex codes by use of Replace Error Code with Exception. Like so many pleasures, exceptions can be used to excess, and they cease to be pleasur- able. Exceptions should be used for exceptional behavior: behavior that is an unexpected error. They should not act as a substitute for conditional tests. If you can reasonably expect the caller to check the condition before calling the operation, you should provide a test, and the caller should use it.

13. Introduce Gateway

You want to interact with a complex API of an external system or resource in a simplified way. Introduce a Gateway that encapsulates access to an external system or resource.

14. Introduce Expression Builder

You want to interact with a public interface in a more fluent manner and not muddy the interface of an existing object. Introduce an Expression Builder and create an interface specific to your application. Introduce Expression Builder 

Refactoring - Organizng Data

1. Self Encapsulated Field

You are accessing a field directly, but the coupling to the field is becoming awkward. Create getting and setting methods for the field and use only those to access the field.

2. Replace Data Value with Object

You have a data item that needs additional data or behavior. Turn the data item into an object: *1. Create the class for the value. Give it an equivalent field to the field in the source class. Add an attribute reader and a constructor that takes the field as an argument. *2. Change the attribute reader in the source class to call the reader in the new class.

3. Change Value to Reference

You have a class with many equal instances that you want to replace with a single object. Turn the object into a reference object.

4. Change Reference to Value

You have a reference object that is small, immutable, and awkward to manage. Turn it into a value object.

5. Replace Array with Object

You have an Array in which certain elements mean different things. Replace the Array with an object that has a field for each element. *1. Create a new class to represent the information in the Array. Give it a method called [] so that callers that read the Array can be changed one by one. Give it a method called []= so that callers that write to the Array can be changed one by one. *2. Construct the new object wherever the Array was instantiated.

6. Replace Hash with Object

You have a Hash that stores several different types of objects, and is passed around and used for more than one purpose. Replace the Hash with an object that has a field for each key.

*1. Create a new class to represent the information in the Hash. Give it a method called [] so that callers that read the Hash can be changed one by one. Give it a method called []= so that callers that write to the Hash can be changed one by one. *2. Construct the new object wherever the Hash was instantiated.

Refactoring - Moving Features from Objects

The most fundamental question in object oriented design is "Where to put responsibilities" Often times issues around these questions resolve these problems simply by using Move Method and Move Field to move the behavior around. If we need to use both, use Move Field first and then Move Method.

Move Method

A method is, or will be, using or used by more features of another class than the class on which it is defined. Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether. Examine all features used by the source method that are defined on the source class. Consider whether they also should be moved.

Move Field

A field is, or will be, used by another class more than the class on which it is defined. Create a new attribute reader (and if necessary, a writer) in the target class, and change all its users.

Extract Class

You have one class doing work that should be done by two. Create a new class and move the relevant fields and methods from the old class into the new class.

Inline Class

A class isn’t doing very much. Move all its features into another class and delete it.

Hide Delegate

A client is calling a delegate class of an object. Create methods on the server to hide the delegate. One of the keys, if not the key, to objects is encapsulation. Encapsulation means that objects need to know less about other parts of the system. Then when things change, fewer objects need to be told about the change—which makes the change easier to make.

Remove the Middle Man

A class is doing too much simple delegation. Get the client to call the delegate directly. In the motivation for Hide Delegate, we talked about the advantages of encapsulating the use of a delegated object. There is a price for this. The price is that every time the client wants to use a new feature of the delegate, you have to add a simple delegating method to the server. After adding features for a while, it becomes painful. The server class is just a middle man, and perhaps it’s time for the client to call the delegate directly.

Refactoring - Composing Methods

Extract Method

is moving duplicate code into a method. Create a new method, and name it after the intention of the method
(name it by what it does, not by how it does it).


Inline Method

is replacing the caller of the method with the body of the method

Inline Temp

Find all references to the temp and replace them with the right-hand side
of the assignment.


Replace Temp with Query

Find all references to the temp and replace them with the right-hand side
of the assignment.


Replace Temp with Chain

removes unneeded creations of temp (variable assignments for method calling expressions) and chain with the right side of the expression, most likely using .self

Introduce Explaining Variable

You have a complicated expression, Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.

Split Temporary Variable

You have a temporary variable assigned to more than once, but it is not a loop variable nor a collecting temporary variable. Make a separate temporary variable for each assignment.

Remove Assignments to Parameters: The code assigns to a parameter. Use a temporary variable instead.

Replace Method with Method Object

You have a long method that uses local variables in such a way that you cannot apply Extract Method. Create a new class, name it after the method.


Extract Surrounding Methods

Step 1: Use Extract Method on one piece of duplication. Name it after the duplicated behavior. This will become our surrounding method. For now the surrounding method will still perform the unique behavior.

Step 2: Modify the calling method to pass a block to the surrounding method.
Copy the unique logic from the surrounding method into the block.


Step 3: Replace the unique logic in the extracted method with the yield keyword.


Step 4: Identify any variables in the surrounding method that are needed by the unique logic and pass them as parameters in the call to yield.


Introduce the Named Parameter

The parameters in a method call cannot easily be deduced from the name of the method you are calling. Convert the parameter list into a Hash, and use the keys of the Hash as names for the parameters.

Dynamic Method Definition

Example: Using def_each to Define Similar Methods

Defining several similar methods is verbose and often unnecessary. For example, each of the following methods is simply calling the state method.

Isolate Dynamic Receptor

A class utilizing methodmissing has become painful to alter. Introduce a new class and move the methodmissing logic to that class.

Move Eval from Runtime to Parse Time

You need to use eval but want to limit the number of times eval is necessary. Move the use of eval from within the method definition to defining the method itself.