Chapter 11. Dealing with Generalization

Generalization produces its own batch of refactorings, mostly dealing with moving methods around a hierarchy of inheritance, or a module hierarchy. Pull Up Method and Push Down Method promote function up and down a hierarchy, respectively. Rather than pushing down a constructor, it is often useful to use Replace Constructor with Factory Method.

If you have methods that have a similar outline body but vary in details, you can use Form Template Method to separate the differences from the similarities.

In addition to moving functionality around a hierarchy, you can change the hierarchy by creating new classes or modules. Extract Module, Extract Subclass, and Introduce Inheritance all do this by forming new elements out of various points. If you find yourself with unnecessary classes or modules in your hierarchy, you can use Collapse Hierarchy or Inline Module to remove them.

Sometimes you find that inheritance is not the best way of handling a situation and that you need delegation instead. Replace Inheritance with Delegation helps make this change. Sometimes life is the other way around and you have to use Replace Delegation with Hierarchy.

It is a good idea to use Replace Abstract Superclass with Module if you never intend to directly instantiate your superclass, to better communicate this intention.

Pull Up Method

You have methods with identical results on subclasses.

Move them to the superclass.

image

Motivation

Eliminating duplicate behavior is important. Although two duplicate methods work fine as they are, they are nothing more than a breeding ground for bugs in the future. Whenever there is duplication, you face the risk that an alteration to one will not be made to the other. Usually it is difficult to find the duplicates.

The easiest case of using Pull Up Method occurs when the methods have the same body, implying there’s been a copy and paste. Of course it’s not always as obvious as that. You could just do the refactoring and see if the tests croak, but that puts a lot of reliance on your tests. I usually find it valuable to look for the differences; often they show up behavior that I forgot to test for.

Often Pull Up Method comes after other steps. You see two methods in different classes that can be parameterized in such a way that they end up as essentially the same method. In that case the smallest step is to parameterize each method separately and then generalize them. Do it in one go if you feel confident enough.

A special case of the need for Pull Up Method occurs when you have a subclass method that overrides a superclass method yet does the same thing.

The most awkward element of Pull Up Method is that the body of the methods may refer to features that are on the subclass but not on the superclass. If the feature is a method, you may be able to generalize the other method. You may need to change a method’s signature or create a delegating method to get this to work.

If you have two methods that are similar but not the same, you may be able to use Form Template Method.

This refactoring also applies to a module hierarchy, where a method is duplicated on two or more classes that include a module. Move the methods onto the module itself.

Mechanics

1. Inspect the methods to ensure they are identical.

image If the methods look like they do the same thing but are not identical, use Substitute Algorithm on one of them to make them identical.

2. If the methods have different signatures, change the signatures to the one you want to use in the superclass.

3. Create a new method in the superclass, copy the body of one of the methods to it, and adjust.

4. Delete one subclass method.

5. Test.

6. Keep deleting subclass methods and testing until only the superclass method remains.

Example

Consider a customer with two subclasses: regular customer and preferred customer (see Figure 11.1).

Figure 11.1 Regular customer and preferred customer.

image

The create_bill method is identical for each class:

image

I copy create_bill from one of the subclasses. I then remove the create_bill method from one of the subclasses and test. I then remove it from the other and test (see Figure 11.2).

Figure 11.2 Inheritance hierarchy after pulling the create_bill method up to the superclass.

image

Push Down Method

Behavior on a superclass is relevant only for some of its subclasses.

Move it to those subclasses.

image

Motivation

Pull Down Method is the opposite of Pull Up Method. I use it when I need to move behavior from a superclass to a specific subclass, usually because it makes sense only there. You often do this when you use Extract Subclass. Pull Down Method is also used to move methods from a module onto a class that includes that module.

Mechanics

1. Declare a method in all subclasses and copy the body into each subclass.

image Use an accessor on the superclass to access any fields on the superclass. Make the accessor protected if public access isn’t required.

2. Remove method from superclass.

3. Test.

4. Remove the method from each subclass that does not need it.

5. Test.

Extract Module

You have duplicated behavior in two or more classes.

Create a new module and move the relevant behavior from the old class into the module, and include the module in the class.

image

image

image

Motivation

There are a number of reasons to use Extract Module, the primary one being the removal of duplication. It’s relatively straightforward to create a new module, move methods to the module, and include the module in the appropriate classes. But Extract Module should be used with care. A module should have a single responsibility, just like a class. The methods within a module should be cohesive: They should make sense as a group. Too often I’ve seen modules become “junk-drawers” for behavior. They are created with the noble goal of removing duplication, but over time they become bloated. The methods don’t make sense together, and it becomes difficult to find a particular piece of behavior. And when existing behavior is hard to find, identifying and removing duplication is an onerous task. A module that is difficult to name without using words like “Helper” or “Assistant” is probably doing too much.

So the question becomes, when do you choose Extract Module over Extract Class? When we’re talking about the removal of duplication, use Extract Class whenever possible. Let’s say you have class A and class B. Behavior X is duplicated in A and B. If you can remove behavior X entirely from class A and B by extracting the behavior to class C and having instances of A and A delegate to an instance of C, then that’s great. A and B both have one less responsibility, the duplication has been removed, and C is now able to be reused and tested in isolation.

If, however, behavior X only makes sense on class A and B, then Extract Module is a better choice. Perhaps a framework you are using looks for the behavior on class A and B, and the introduction of delegation would be messy.

Mechanics

1. Create a blank module; include the module into the original classes.

2. Start with the instance methods. One by one, use Pull Up Method to move the common elements to the module.

image If you have methods in the including classes that have different signatures but the same purpose, use Rename Method to get them to the same name and then use Pull Up Method.

image If you have methods with different bodies that do the same thing, you may try using Substitute Algorithm to copy one body into the other. If this works, you can then use Pull Up Method.

3. Test after each pull.

4. For class methods that you want to call directly on the module, use Move Method just as you did for the instance methods. Change the callers to call the method using the module reference.

5. If you have class methods that you want to call on either of the including classes, create an included hook on the module. Move the class method definitions to the included hook.

6. Test after each change.

7. Examine the methods left on the including classes. See if there are common parts, if there are you can use Extract Method followed by Pull Up Method on the common parts. If the overall flow is similar, you may be able to use Form Template Method.

Example

In this example, we are using a framework that makes use of a before_save hook to perform work before the object is saved to a database. In our case, we want to update an account number on both the Bid and Sale objects before they are saved to the database.

image

At this point I ask myself whether I should use Extract Class or Extract Module. Since we want our bid and sale objects to respond to before_save (so that the framework can call it), we’re stuck with writing some sort of code to make Bid and Sale respond to before_save. We could conceivably implement capture_account_number on Bid and Sale, and use it to delegate to another object, but this object would have to be aware of the caller so that it could set the account_number. We would be delegating to an object only to have it call back to our bid and sale, which is undesirable. Extract Module is the way to go.

Let’s start by creating an AccountNumberCapture module.

image

Next we’ll include this module into Bid and Sale.

image

We’re now ready to perform Move Method on the first (and only) instance method. We remove it from Bid, and add it to AccountNumberCapture.

image

All going well, our tests should pass.

We don’t have any class methods to move—if we did, we’d have to decide whether we wanted to call them directly on the module (for example, AccountNumberCapture.my_class_method), or on one of the including classes (for example, Bid.my_class_method). If we choose the former, we can move the class method in the same way as we did the instance methods. But if the class method calls something specific on the including class, the method has to go on the including class itself. For that, we would need an included hook. The same applies to our before_save class annotation, which needs to be executed on the including class, not on the AccountNumberCapture module. We’ll need to open up the class that’s including our module using class_eval and make the call to before_save.

image

This can be an awkward step at first if you’re not used to opening up classes in this fashion, but if all goes well, the tests should pass.

We can then remove the duplicated methods from the Sale class.

The full definition of AccountNumberCapture looks like this:

image

Inline Module

The resultant indirection of the included module is no longer worth the duplication it is preventing.

Merge the module into the including class.

Motivation

Modules introduce a level of indirection—to find the behavior you first have to go to the class definition, find the name of the included module, and then go to the module definition. This indirection is worthwhile if you can remove duplication. But if a module is no longer pulling its weight—if the level of indirection is not worth the savings in duplication—merge the module into the class that is including it.

Mechanics

1. Use Push Down Method to move all the behavior out of the module onto the class that is including the module.

2. Test with each move.

3. Remove the empty module.

4. Test.

Extract Subclass

A class has features that are used only in some instances.

Create a subclass for that subset of features.

image

Motivation

The main trigger for use of Extract Subclass is the realization that a class has behavior used for some instances of the class and not for others. Sometimes this is signaled by a type code, in which case you can use Replace Type Code with Polymorphism, Replace Type Code with Module Extension, or Replace Type Code with State/Strategy. Another alternative to Extract Subclass is Extract Class. This is a choice between delegation and inheritance. Extract Subclass is usually simpler to do, but it has limitations. You can’t change the class-based behavior of an object once the object is created. You can change the class-based behavior with Extract Class simply by plugging in different components. Another limitation of subclasses is that you’re also only able to represent one variation, as a given class in Ruby can only inherit from one superclass directly. If you want the class to vary in several different ways, you have to use delegation or module extension for all but one of them.

Mechanics

1. Define a new subclass of the source class.

2. Look for places where the subclass should be created instead of the superclass.

image If you need conditional logic to determine which type to create, consider using Replace Constructor with Factory Method.

3. One by one use Push Down Method to move features onto the subclass.

4. Look for any field that designates information now indicated by the hierarchy (usually a boolean or type code). Eliminate it by using Self Encapsulate Field and replacing the getter with polymorphic constant methods. All users of this field should be refactored with Replace Conditional with Polymorphism.

image For any methods outside the class that use an accessor, consider using Move Method to move the method into this class; then use Replace Conditional with Polymorphism.

5. Test after each push down.

Example

I’ll start with a JobItem class that determines prices for items of work at a local garage:

image

image

I extract a LaborItem subclass from this class because some of the behavior and data are needed only in that case. I begin by creating the new class:

image

The first thing I need is a constructor for the labor item because job item does not have a no-arg constructor. For this I copy the signature of the parent constructor:

image

Although this may be enough to get the tests to pass, the constructor is messy; some arguments are needed by the labor item, and some are not. I deal with that later.

The next step is to look for calls to the constructor of the job item, and to look for cases where the constructor of the labor item should be called instead. So statements like:

j1 = JobItem.new(0, 5, true, kent)

become

j1 = LaborItem.new(0, 5, true, kent)

Now is a good time to clean up the constructor parameter lists. I work with the superclass first. I use a default value of false for is_labor so that I can change the callers one-by-one:

image

Callers can now take advantage of the default values:

j2 = JobItem.new(10, 15)

Once I’ve run the tests, I use Remove Parameter on the subclass constructor:

image

Now I can start pushing down the features of the job item. I begin with the methods. I start with using Push Down Method on the employee attribute reader:

image

I can clean up the constructors so that employee is set only in the subclass into which it is being pushed down:

image

The field @is_labor is used to indicate information that is now inherent in the hierarchy. So I can remove the field. The best way to do this is to first use Self Encapsulate Field and then change the accessor to use a polymorphic constant method. A polymorphic constant method is one whereby each implementation returns a (different) fixed value:

image

Then I can get rid of the @labor field.

Now I can look at users of the labor? methods. These should be refactored with Replace Conditional with Polymorphism. I take the method:

image

and replace it with

image

Because unit_price is used only by items that are nonlabor (parts job items), I can use Extract Subclass on job item again to create a parts item class. When I’ve done that, the job item class will not be instantiated explicitly. It will only be used as the superclass of LaborItem and PartsItem. In this case, I could use Replace Abstract Superclass with Module.

Introduce Inheritance

You have two classes with similar features.

Make one of the classes a superclass and move the common features to the superclass.

image

Motivation

Duplicate code is one of the principal bad things in systems. If you say things in multiple places, then when it comes time to change what you say, you have more things to change than you should.

One form of duplicate code is two classes that do similar things in the same way or similar things in different ways. Objects provide a built-in mechanism to simplify this situation with inheritance. However, you often don’t notice the commonalities until you have created some classes, in which case you need to create the inheritance structure later.

An alternative to Introduce Inheritance is Extract Class. The choice is essentially between inheritance and delegation. Inheritance is the simpler choice if the two classes share interface as well as behavior. If you make the wrong choice, you can always use Replace Inheritance with Delegation later.

If you can’t use Extract Class, consider Extract Module. If you never intend to directly instantiate the superclass, the use of a module instead will better communicate this intention. If you make the wrong choice, it’s no big deal—you can always use Replace Abstract Superclass with Module later.

Mechanics

1. Choose one of the classes to be a superclass. Make the other classes inherit it.

2. One by one, use Pull Up Method to move common elements to the superclass.

image If you have subclass methods that have different signatures but the same purpose, use Rename Method to get them to the same name and then use Pull Up Method.

image If you have methods with different bodies that do the same thing, you may try using Substitute Algorithm to copy one body into the other. If this works, you can then use Pull Up Method.

3. Test after each pull.

4. Examine the methods left on the subclass. See if there are common parts, if there are you can use Extract Method followed by Pull Up Method on the common parts. If the overall flow is similar, you may be able to use Form Template Method.

Example

Again we’ll use the mountain bike example. An instance of the MountainBike class has no suspension, whereas FrontSuspensionMountainBike has front suspension:

image

image

There are a couple of areas of commonality here. First, both kinds of bicycles have the wheel_circumference and off_road_ability calculation, as well as the tire_diameter attribute and TIRE_WIDTH_FACTOR. wheel_circumference is identical for both, whereas off_road_ability is slightly different. FrontSuspensionMountainBike looks like a specialization of MountainBike so I’ll make MountainBike the superclass:

image

Now I begin to pull up features to the superclass. I can start by deleting the wheel_circumference method, tire_diameter, and TIRE_WIDTH_FACTOR, which are exactly the same on both classes. Then I can make off_road_ability in FrontSuspensionMountainBike use MountainBike:

image

Collapse Hierarchy

A superclass and subclass (or module and the class that includes the module) are not very different.

Merge them together.

image

Motivation

If you have been working for a while with a class or module hierarchy, it can easily become too tangled for its own good. Refactoring the hierarchy often involves pushing methods and fields up and down the hierarchy. After you’ve done this you can well find you have a subclass or module that isn’t adding any value, so you need to merge the hierarchy together.

Mechanics

We describe here the mechanics for merging an inheritance hierarchy, but the refactoring is the same for modules.

1. Choose which class is going to be removed: the superclass or the subclasses.

2. Use Pull Up Method or Push Down Method to move all the behavior of the removed class to the class with which it is being merged.

3. Test with each move.

4. Adjust references to the class that will be removed to use the merged class.

5. Remove the empty class.

6. Test.

Form Template Method

You have two methods in subclasses that perform similar steps in the same order, yet the steps are different.

Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up.

image

Motivation

Inheritance is a powerful tool for eliminating duplicate behavior. Whenever we see two similar methods in a subclass, we want to bring them together in a superclass. But what if they are not exactly the same? What do we do then? We still need to eliminate all the duplication we can but keep the essential differences.

A common case is two methods that seem to carry out broadly similar steps in the same sequence, but the steps are not the same. In this case we can move the sequence to the superclass and allow polymorphism to play its role in ensuring the different steps do their things differently. This kind of method is called a template method [Gang of Four].

In Ruby, it is also possible to Form Template Method using the extension of modules. The class that extends the modules plays the role of the superclass, housing the sequence, while the modules implement the specific behavior.

Mechanics

1. Decompose the methods so that all the extracted methods are either identical or completely different.

2. Use Pull Up Method to pull the identical methods into the superclass (when using inheritance) or the class that extends the modules (when using module extension).

3. For the different methods use Rename Method so the signatures for all the methods at each step are the same.

image This makes the original methods the same in that they all issue the same set of method calls, but the subclasses/modules handle the calls differently.

4. Test after each signature change.

5. Use Pull Up Method on one of the original methods.

6. Test.

7. Remove the other methods. Test after each removal.

Example 1: Template Method Using Inheritance

I finish where I left off in Chapter 1, “Refactoring, a First Example.” I had a customer class with two methods for printing statements. The statement method prints statements in ASCII:

image

while the html_statement does them in HTML:

image

Before I can use Form Template Method I need to arrange things so that the two methods are subclasses of some common superclass. I do this by using a method object [Beck] to create a separate strategy hierarchy for printing the statements (refer to Figure 11.3).

image

Figure 11.3 Using a strategy for statements.

image

Now I use Move Method to move the two statement methods over to the subclasses:

image

image

As I moved them I renamed the statement methods to better fit the strategy. I gave them the same name because the difference between the two now lies in the class rather than the method. (For those trying this from the example, I also had to add a rentals method to customer and relax the visibility of total_charge and total_frequent_renter_points.

With two similar methods on subclasses, I can start to use Form Template Method. The key to this refactoring is to separate the varying code from the similar code by using Extract Method to extract the pieces that are different between the two methods. Each time I extract I create methods with different bodies but the same signature.

The first example is the printing of the header. Both methods use the customer to obtain information, but the resulting string is formatted differently. I can extract the formatting of this string into separate methods with the same signature:

image

image

I test and then continue with the other elements. I did the steps one at a time. Here is the result:

image

image

Once these changes have been made, the two value methods look remarkably similar. So I use Pull Up Method on one of them, picking the text version at random:

image

I remove the value method from text statement and test. When that works I remove the value method from the HTML statement and test again. The result is shown in Figure 11.4.

Figure 11.4 Classes after forming the template method.

image

After this refactoring, it is easy to add new kinds of statements. All you have to do is create a subclass of statement that implements the three methods.

Technically, if the Statement class was never to be instantiated directly, I would make it a module. (See Replace Abstract Superclass with Module later in this chapter for an explanation.) I’d then include the Statement module in each of the HtmlStatement and TextStatement classes. But the traditional Template Method pattern is done with inheritance, so I thought an example would be useful.

Example 2: Template Method Using Extension of Modules

For this example, we’ll go back to the code as it stood at the end of Chapter 1:

image

Similarly to the inheritance example, I first create a Statement class. Instead of creating two subclasses of Statement, I create two modules to house the unique behavior:

image

I use Move Method to move the two statement methods over to the modules:

image

image

As with the inheritance example, I use Extract Method on any unique behavior:

image

image

The final step is to pull up the value method.

image

image

And finally we’re left with the structure shown in Figure 11.5.

Figure 11.5 Using dynamic extension of modules to implement Form Template Method.

image

Notice that the implementation using extension of modules is similar to the inheritance example. So why use extend instead of inheritance? The answer is that you would use extend if the modules you were creating could be used to extend various classes.

For example, let’s imagine that the next requirement of our application is to display the preceding information for only the previous month. The current statement class gives a list of each rental associated with a customer for all months. To satisfy our new requirement we could create a MonthlyStatement class similar to the following code.

image

The advantage of module extension is now clear: Because a given class in Ruby can inherit directly from only one other class, the inheritance approach would have forced us to implement both a TextMonthlyStatement class and an HtmlMonthlyStatement class, instead of our MonthlyStatement class. Each new subclass would have had to decide whether to output the rental based on the rental date, which is not ideal. The module extension approach allows us to avoid the complexity of another level of inheritance and consolidates the date-checking code into one method.

image

Replace Inheritance with Delegation

A subclass uses only part of a superclass interface or does not want to inherit data.

Create a field for the superclass, adjust methods to delegate to the superclass, and remove the subclassing.

image

Motivation

Inheritance is a wonderful thing, but sometimes it isn’t what you want. Often you start inheriting from a class but then find that many of the superclass operations aren’t really true of the subclass. In this case you have an interface that’s not a true reflection of what the class does. Or you may find that you are inheriting a whole load of data that is not appropriate for the subclass. Or you may find that there are protected superclass methods that don’t make much sense with the subclass.

You can live with the situation and use convention to say that although it is a subclass, it’s using only part of the superclass function. But that results in code that says one thing when your intention is something else—a confusion you should remove.

By using delegation instead, you make it clear that you are making only partial use of the delegated class. You control which aspects of the interface to take and which to ignore. The cost is extra delegating methods that are boring to write but are too simple to go wrong.

Mechanics

1. Create a field in the subclass that refers to an instance of the superclass. Initialize it to self.

2. Change each method defined in the subclass to use the delegate field. Test after changing each method.

image You won’t be able to replace any methods that invoke a method on super that is defined on the subclass, or they may get into an infinite recurse. These methods can be replaced only after you have broken the inheritance.

3. Remove the subclass declaration and replace the delegate assignment with an assignment to a new object.

4. For each superclass method used by a client, add a simple delegating method.

5. Test.

Example

One of the classic examples of inappropriate inheritance is inheriting from a collection. Here I have a Policy class that inherits from Hash. Each Hash element is an Array of Rules, and Policy gives the Hash an Arraylike interface by implementing the << operator:

image

The Rule class has an attribute and default value:

image

Looking at the users of Policy, I realize that clients do only five things: <<, apply, [], size, and empty?. The latter three are inherited from Hash.

I begin the delegation by creating a field for the delegated Hash. I link this field to self so that I can mix delegation and inheritance while I carry out the refactoring:

image

Now I start replacing methods to get them to use the delegation. I begin with <<:

image

I can test here, and everything will still work. Now the apply method:

image

Once I’ve completed these subclass methods, I need to break the link to the superclass:

image

I then extend Forwardable to add simple delegating methods for superclass methods used by clients:

image

Now I can test. If I forgot to add a delegating method, the tests will tell me.

Replace Delegation with Hierarchy

You’re using delegation and are often writing many simple delegations for the entire interface.

Make the delegate a module and include it into the delegating class.

image

Motivation

This is the flip side of Replace Delegation with Inheritance, though generally we’ll end up with a module hierarchy rather than inheritance.

If you find yourself using all the methods of the delegate and are sick of writing all those simple delegating methods, you can switch back to a hierarchy pretty easily.

One situation to beware of is that in which the delegate is shared by more than one object and is mutable. In this case you can’t replace the delegate with a hierarchy because you’ll no longer share the data. Data sharing is a responsibility that cannot be transferred back to a module hierarchy. When the object is immutable, data sharing is not a problem, because you can just copy and nobody can tell.

Mechanics

1. Make the delegate a module. Include the module in the delegating object.

2. Remove the simple delegation methods.

3. Set the delegate instance variable to self.

4. Test.

5. Replace all other delegations with calls to the object itself.

6. Remove the delegate field.

Example

A simple Employee delegates to a simple Person:

image

image

The first step is to make Person a module and include it into Employee:

image

The next step is to make the delegate field refer to self. I must remove all simple delegation methods such as name and name=. If I leave any in, I will get a stack overflow error caused by infinite recursion. In this case I need to remove name and name= from Employee. Now that Employee includes the Person module, these methods have already been mixed into the Employee, so it is just a matter of removing the def_delegators declaration.

image

Next I can change the methods that use the delegate. I switch them to use calls to implicit self:

image

Once I’ve gotten rid of all methods that use delegate methods, I can get rid of the @person field.

Replace Abstract Superclass with Module

You have an inheritance hierarchy, but never intend to explicitly instantiate an instance of the superclass.

Replace the superclass with a module to better communicate your intention.

image

Motivation

In Java, it is possible to designate a class as abstract to prevent objects of that class from being instantiated explicitly. There is no such feature in Ruby. Writing intentional code is important, and it would be nice if we could communicate that instances of our abstract superclass are not meant to be instantiated directly. We could write some code to raise an error when the constructor is invoked, but if instead we replace the abstract superclass with a module, we can communicate our intention in a more idiomatic fashion.

Mechanics

1. If the superclass has any class methods that you want to call using the subclass class, define an inherited hook and use the hook to move the class methods onto the subclasses themselves.

2. Test.

3. Make the class a module, and replace the inheritance declaration with an include in each of the base classes.

4. Make the inherited hook an included hook.

5. Test.

Example

Let’s start with some subclass objects that we are using to construct SQL joins:

image

They can be used like so:

image

And we have a class method for returning all joins for a given table:

image

The superclass looks like this:

image

When we change Join to a module, the joins_for_table method will not be available using the InnerJoin.joins_for_table syntax. To enable this, we have to add the joins_for_table method to both of the subclasses. To do so without duplicating the method, we can use the inherited hook. We define the inherited hook on the Join class, and within the definition we open up the class that is doing the inheriting, and add the joins_for_table method:

image

If we’re successful, our tests should pass.

Then we can make Join a module:

image

And include the Join module into our subclasses, removing the inheritance as we go:

image

Finally, our inherited hook will no longer be executed, because we’re not inheriting—we’re including. Luckily for us, Ruby has an included hook, with similar syntax:

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset