Agile Software Development
Refactoring
Christopher Jones Fall, 2020-2021
School of Computing, DePaul University
Motivation
1/26
Noun
“A change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.” [1]
Verb
“To restructure software by applying a series of refactorings without changing its observable behavior.” [1]
2/26
3/26
4/26
Bad Smells
5/26
Duplicated Code
Find ways to avoid duplicate code. This is the principle behind DRY (Don’t Repeat Yourself).
The general approach is to move the common code into its own method or class and reuse it.
6/26
Long Method
Long methods are hard to:
• Test.
• Understand.
It’s often possible to extract components of large methods into smaller ones that are simpler to test, understand and maintain.
7/26
Long Parameter List
Long parameter lists are often hard to understand and maintain.
In general it’s better practice to pass objects that contain the data rather than the data itself.
Be careful about following this guidance on remote methods. Object serialization my introduce unacceptable performance and network overhead.
8/26
Divergent Change
Occurs when a single class is changed in common ways for different reasons. For example, changing some methods of a class to support new databases and other methods in the same class to support integration to a new service provider. This is a good indicator that this single class should really be two classes.
Some code quality metrics, like LCOM, can help identify this kind of problem since such classes will typically not be cohesive.
9/26
Shotgun Surgery
Each time you want to make a change, you need to make lots of smaller changes in many different classes.
This is a good sign that all of those smaller methods can be aggregated into a single class.
10/26
Feature Envy
A method of one class requires lots of data from another class in order to perform its job.
Usually a sign that the method is in the wrong class.
11/26
Data Clump
Data clumps are pieces of discrete data that are commonly found together.
It may make sense to aggregate such data elements into a new class.
12/26
Primitive Obsession
The use of (multiple) primitive values rather than using a class-based representation.
The best solution for this smell is to transform those small, primitive fields into classes where any useful, specialized functionality can be incorporated.
13/26
Switch Statements
Switch statements often lead to duplicate logic scattered around the code.
A better approach would be to define an appropriate class hierarchy and rely on polymorphism to handle the different cases.
14/26
Parallel Inheritance Hierarchies
Each time you create a new subclass in one hierarchy you have to create a new subclass in another.
Sometimes this is desirable, for instance to support various design patterns. In other cases, collapsing the two hierarchies into a single one might make more sense.
15/26
Lazy Class
A class isn’t responsible for very much.
Subclasses can be merged into super classes or inlined into their client classes.
16/26
Speculative Generality
A class shouldn’t have lots of hooks and logic for dealing with speculative future requirements.
Get rid of all such functionality since it makes things harder to understand.
17/26
Temporary Fields
One or more fields are used only during certain processing steps and are unused otherwise.
Move those fields into a new class.
18/26
Message Chains
One object must traverse a long chain of accessor methods to get at the data it really needs. This tightly couples the client object to the structure of hierarchy and the organization of the objects.
It may be possible to move the method closer to the caller by either physically moving it, or by providing a delegate method.
19/26
Middle Man
Too many delegating methods are located in a single class.
It may be worthwhile to get rid of the middle-man class and have the caller interact with the real objects.
20/26
Inappropriate Intimacy
Two classes know to much about one another’s inner workings.
Rework the code to enforce stricter encapsulation. Possibly replace inheritance with delegation.
21/26
Alternative Classes with Different Interfaces
Multiple methods on the same class that do the same thing have different signatures.
Consolidate the methods. Possibly construct new superclasses to inherit shared functionality.
22/26
Incomplete Library Class
Libraries may not provide all of the features or capabilities that we need.
Introducing new methods or classes to an external library isn’t a good idea. We instead use inheritance and the constructive use of local methods to extend the library’s behavior.
23/26
Data Class
This is the classic “Value Object” structure where a class has data members and associated accessors and mutators.
The class needs more responsibility ensure that other classes are not performing tasks this class should be doing for itself.
24/26
Refused Bequest
A subclass doesn’t want the implementation of its parent.
This may be a sign that the hierarchy is wrong. If it’s causing confusion, address the issue. If not, it may be easier to do nothing.
It’s a different issue if the subclass doesn’t want the parent’s interface. Then you’re likely looking at a case of bad inheritance that should be replaced by delegation.
25/26
References i
[1] Martin Fowler.
Refactoring.
Addison Wesley, 2001.
26/26