No Fear Legacy “Fat Class” Refactoring
using S.O.L.I.D.’s Interface Segregation Principle (ISP)
Weight Loss | Fat Class | Print Example | Fat Method | |
ISP Weight Loss | ISP Trick | How It Works | Wisdom Pearl | |
Select a content section anchor button and then use the Browser’s back button to return to selections |
A Class Weight Loss Program
Brown Field code in Legacy Projects offers the Modern Developer the opportunity to add real value to the project for the Client.
Older Legacy Code rarely complies with the Design Principles, Design Patterns and Code Standards of the Modern Developer’s Manifesto.
The Modern Developer’s Boy Scout Principle States:
“The Modern Developer should always leave a Software Entity in better shape than it was in when they entered the Legacy Code environment”
This is Easier said than Done in most Environments
Most Brown Field projects may not have the benefits of Test Driven Development’s with its Unit and Integration tests for assembly components.
The Lack of Unit and Integration Tests
… Creates a ‘Culture of Fear of Refactoring’
Within the Maintenance Community
This fear inhibits a good developer from exercising the Boy Scout Principle for fear of “Breaking Code” that caused bugs that cannot be easily be regressively tested and validated.
The Biggest Object Offenders
are Fat Classes and Fat Methods
What is a Fat Class?
A Class Object is Fat if it Violates the
Single Responsibility Principle (SRP)
If a Class Object it trying to provide more than One Responsibility, for the Calling Client’s Method, then it is considered a Fat Class.
The developer should clearly define the Business Semantic in the naming of the Class Object and ensure that any dependencies are abstracted from the Class’s Responsibility.
This Does Not Mean the Class
Can Only Do One Thing!
What this means is that the class understands the difference between its Responsibility and its required Dependencies.
The Responsibility and Dependency Principle
is used to support its Single Responsibility
The Print Fat Class
The Client had asked for a simple application to print some data from a text file to the computer screen for easy validation of its content.
The scope of the project has now changed and the Client wishes to add enhanced capabilities to the original deliverable.
The Original User Story:
As a Administrator of Documents
I want a quick and easy tool to verify the contents of a selected document on my computer screen
So that I know that I am about to process the correct document
The New User Story:
As a Administrator of Documents
I want a quick and easy tool to verify the contents of a selected document on my computer screen and print the results to a printer, if required
So that I know that I am about to process the correct document and can work with a hard copy of the document
The Original Solution:
The original solution had four responsibilities:
- Retrieve the data from a selected file on a file server disk drive: An I/O operation
- Format the data for display: Format for a computer screen
- Select the output device: A Console application display
- Display the Retrieved Data (1) that is Formatted for a Computer Screen (2) from a Console Application(3) to the User’s Computer Screen(4)
The developer chose to create a single Console application class and provide three of requirements stated above in a method that was called by the “Main” method of the Console Application.
Functionally the Solution is Correct, it Worked Fine
… Until a Change Request was Created to Add the New Functionality!
The New Requirements Refactoring:
The maintenance Developer needs to refactor the original solution into compliance with the Single Responsibility Principle and extract Classes that support the Open / Closed Principle for the dependencies that the new User Story requires.
The Class Responsibilities:
- The Console Application Class – This Class has three functions that support its Single Responsibility of Displaying Information to the User’s computer screen
- Select the display device
- The Computer Screen
- Paper Print out to the Printer
- Get the File Name to Display
- If Output selection is Computer Screen: Display the File Contents on the User’s computer screen
- Select the display device
- The Output Device Class – This Class has the responsibility to service the Console Applications request for the User’s selected Output Device and File Name as parameters dependency in the Method constructor using the IOutput Interface abstraction for the selected Output Device’s concrete implementation of the selected Output Type
- Deliver the formatted data to the Console Application if the selection is Display on Computer Screen
- Deliver a Success Message that the File Data was successfully Printed
- The Format Class – This Class has the responsibility of returning the Selected Format to the Output Device Class
- Calls the concrete Class that implements the IOutput Interface for the selected output
- Delivers the results to the Output Device Class
- The Printer Class – This Class manages all the logistics of getting data to paper, impairments the IOutput interface
- Printer Section
- Printer Setup
- Data Formatting
- Printing Data to Paper
- The Screen Class –This Class manages all the logistics of getting data to the Computer Screen, impairments the IOutput interface
- Create the “Console Writes”
- Creates a readable display for the Format Class
- The IOutput Interface – This Interface supports an unlimited amount of output devices
- The abstraction for the Printer concrete class
- The abstraction for the Screen concrete class
This refactor architecture creates the ability to easily ass new printers and output medians such as XML and Cloud Services using the IOutput interface abstraction.
The solution complies with Single Responsibility, the Open/ Closed and the Interface Segregation Principles. It also complies with the Responsibility and Dependency and Don’t Repeat Yourself Principles.
The new refactoring is scalable and extensible and will lower the cost of ownership of this solutions for the Client over the Software Development Lifecycle
What is a Fat Method
A Method is Fat if it
Violates the Single Responsibility Principle
Or the Open / Closed Principle
A Class Method has One Responsibility and that is to Service the Calling Class Method’s Request.
The Class Method consumes the parameters, as dependencies, to produce the calling method’s expected return results.
The Class Method does not have the responsibility to Create the Requirements to deliver the expected results: Just to deliver the expected results.
A Class Method that Complies with the
Single Responsibility Principle
Delegates its Dependency Responsibilities
Responsibility delegation is accomplished through Private Class Helper Methods or Public Utility Classes.
If a Dependency has a One and Only One Usage then it is encapsulated within a Class Helper Region.
An extracted refactored method is created with a Private Access Modifier.
This method is only available within the Current Class.
If the Dependency can be used in Two or More Classes it is refactored to a Common Utility Class as a Public Helper Method.
The ISP Weight Loss Program
The Interface Segregation Principle states:
“Clients of Interfaces should not be forced
to Implement Interface Contracts
they do not require”
This principle is violated, in spirit, by every Fat Class.
These Fat Classes violate the Single Responsibility Principle even if Interfaces are implemented in the Class.
You use an IDE tool, like ReSharper or Visual Studio, to create an Interface that represents all of the responsibilities currently in the Class.
The Fat Class then implements all of its Method Signature Contracts as a validation of the Fat Interface.
This Interface would Show the
Violations in Responsibilities
There would be many Interface Contracts that clearly are not related to the original intent of the Class Object.
The ISP Refactoring Trick
You really don’t really want to copy and propagate bad code and bad practices.
If you feel you need to add additional Code Smells to a Fat Class use the ISP Refactoring Trick.
You can use the IDE Extract Interface refactoring
as your Weight Reduction Tool
You will create an Interface that will be implemented in a New Class that consumes a copy of the related methods and other Software Entities within the “Fat Class”.
You code will Implement the Interface and you will build the contract’s concretes, as required.
The ISP Refactoring Trick
… Allows you to Comply with your Deliverable Request
…… Without Adding to the Current Code Smell
How the Class Weight Loss Program Works
The Class Weight Loss Program works using the ISP Refactoring Trick:
-
Open the Fat Class for Refactoring
-
Use ReSharper to create the Interface Abstractions
-
Organize the collection of contracts into separate Interfaces
-
Separate Interface contracts signatures into responsibilities
-
Create a new Interface file for each responsibility collection
-
Add all the new Interfaces to the Fat Class
-
Compile the Class
-
Run the application / tests to validate normal functionality
-
At this point you have defined the
Single Responsibilities that the Fat Class
is providing to the calling Classes
You have two choices at this point:
-
Extract all the related dependencies into New Classes, implement the single Interface and fix all compile errors caused by the incorrect Namespace and Class Type pointers:
Best Choice but can time-consuming in some situations
-
Extract just the dependencies you require into a New Class and implement the single Interface. Use this new Class as the Type for your new design solution:
No regressive issues as legacy code uses the “Fat Class” and your code uses the new implementation requirements
Choice number two creates a bridge for future refactoring though the organization of related dependency signature contract Interfaces.
As new requirements require changes to the Fat Class then solution choice two can be implemented and validated during Code Review.
You have now Complied with the
Boy Scout Principle
as you have left the Environment
Better than when you Entered It
Compliance with the Interface Segregation Principle supports a lower Total Cost Of Ownership
Creating “Skinny” Classes and Methods
Reduces the Cost of Maintenance and the
Learning Curve of a New Developer and
that Developer May be You in Six Months
Wisdom Pearl #131: Don’t Propagate Bad Code
Be Part of the Solution … Not Part of the Problem
… Don’t add to a Fat Class … Use Interface Refactoring
Latest posts by Brad Huett (see all)
- DevOps: A Bridge to Your DevOps Culture - March 25, 2016
- Embracing Test Driven Development (TDD) - March 25, 2016
- DevOps: Delivering Agile Projects - March 25, 2016