Module 3. Smells Between Classes Course: Refactoring
Overview: Smells Between Classes Data Inheritance Responsibility Accommodating Change Library Classes
Lesson: Data Primitive Obsession Data Class Data Clump Temporary Field
Primitive Obsession Symptoms Uses of the primitive or near-primitive types (int, float, String, etc.) Constants and enumerations representing small integers String constants representing field names Causes Missing class Simulated types Simulated field accessors What to Do Replace Data Value with Object Replace Type Code with Class; Replace Type Code with Subclass; Replace Type Code with State/Strategy; Replace Conditional with Polymorphism Replace Array with Object Payoff Improves communication.
Data Class Symptoms class consists only of public data members depend on the mutability and representation of the class Causes realize that some data is part of an independent object What to Do Encapsulate Field Remove Setting Methods Encapsulate Collection Look at each client of the object. Payoff Improves communication.
Data Clump Symptoms the same items frequently appear together in classes and parameter lists. there are groups of fields and methods together within the class groups of field names start or end with similar substrings. Causes The items are typically part of some other entity What to Do Extract Class. Introduce Parameter Object Preserve Whole Object Move Method, etc Payoff Improves communication. May expose duplication. Usually reduces size.
Temporary Field Symptoms A field is set only at certain times, and it is null (or unused) at other times. Causes an algorithm that passes around information through the fields What to Do Extract Class Payoff Improves communication and clarity.
Exercise: Alternative Representations Proper Names (Challenging)
Lesson: Inheritance Refused Bequest Inappropriate Intimacy (Subclass Form) Lazy Class See also these other smells related to inheritance: Simulated Inheritance (Switch Statement) Parallel Inheritance Hierarchies Combinatorial Explosion
Refused Bequest Symptoms honest refusal implicit refusal through a handle to the subclasses. Inheritance doesn't really make sense Causes a class may inherit without really intending the class to be substitutable a conscious decision to let subclasses deny use of some features What to Do you might decide to leave it as is. Replace Inheritance with Delegation. Extract Subclass, Push Down Field, and Push Down Method
Inappropriate Intimacy (Subclass Form) Symptoms A class accesses internal (should-be-private) parts of its parent. Causes Parent and child classes be more coupled together What to Do Self Encapsulate Field. Form Template Method. Replace Inheritance with Delegation. Payoff Reduces duplication. Often improves communication. May reduce size.
Lazy Class Symptoms A class isn't doing. Causes all responsibilities have been moved to other places What to Do Collapse Hierarchy. Inline Class. Payoff Reduces size. Improves communication. Improves simplicity.
Exercise: Collection classes Real application
Lesson: Responsibility Feature Envy Inappropriate Intimacy (General Form) Message Chains Middle Man
Feature Envy Symptoms A method seems to be focused on manipulating the data of other classes rather than its own. Causes This is very common among clients of current and former data classes, but you can see it for any class and its clients. What to Do Move Method Extract Method
Inappropriate Intimacy (General Form) Symptoms One class accesses internal (should-be-private) parts of another class Causes The two classes probably became intertwined a little at a time. There may be a missing class that should mediate between them. What to Do Move Method and Move Field Extract Class and Hide Delegate Change Bidirectional Reference to Unidirectional If a subclass is too coupled to its parent Self Encapsulate Field. Form Template Method. Replace Inheritance with Delegation.
Message Chains Symptoms You see calls of the form: a.b().c().d() Causes the problem is that this couples both the objects and the path to get to them. What to Do Extract Method and Move Method Use Hide Delegate
Middle Man Symptoms object:f() { delegate.f(); } Causes result from applying Hide Delegate to address Message Chains What to Do Remove Middle Man Replace Delegation with Inheritance. Payoff Reduces size.
Exercise: Middle Man Account Cart
Exercise: Hide delegate
Lesson: Accommodating Change Divergent Change Shotgun Surgery Parallel Inheritance Hierarchies Combinatorial Explosion
Divergent Change Symptoms You find yourself changing the same class for different reasons. Causes The class picks up more responsibilities as it evolves What to Do Extract Class Extract Superclass or Extract Subclass
Shotgun Surgery Symptoms Simple change requires to change several classes. Causes One responsibility is split among several classes. Elimination Divergent Change. What to Do Extract Class Move Field and Move Method.
Parallel Inheritance Hierarchies Symptoms required subclass in another hierarchy. subclasses have the same prefix a special case of Shotgun Surgery Causes The hierarchies probably grew in parallel, a class and its pair being needed at the same time. What to Do Move Field and Move Method Payoff Reduces duplication. May improve communication. May reduce size.
Combinatorial Explosion Symptoms you have to introduce multiple classes at various points of the hierarchy. you notice that each layer of the hierarchy uses a common set of words Causes independent decisions instead get implemented via a hierarchy. What to Do Replace Inheritance with Delegation Tease Apart Inheritance
Exercise: Divergent Changes (csv writer) Shotgun Surgery Documents
Exercise: Shotgun Surgery In code you have access to, find examples of this problem. Some frequent candidates: Configuration information. Logging. Persistence. Sometimes it takes two calls on an object to get something common done, and this two-step process is used in several places.
Lesson: Library Classes Incomplete Library Class
Symptoms library has no some features Causes the author of the library class didn't anticipate your need What to Do contact owner Introduce Foreign Method Introduce Local Extension introduce a layer covering the library
Exercise:
Redraw this as a UML package diagram showing dependencies. Explain how the bulk of your code does or does not depend on the library code in each of these situations. What effects does this layering have in terms of: Conceptual integrity? Portability? Performance? Testing?
Exercise:
Review Data Inheritance Responsibility Accommodating Change Library Classes
Gen-A-Refactoring (—) in combinations that don't make sense, (+) in those that are in Fowler's Refactoring catalog (*) in those that make sense but aren't in the catalog.