Divergent Change
Description (What)
Divergent Change is when you want to make changes to a field or a class as whole and end up having to also make changes to methods unrelated to that change in the class.
How to Locate It (Where)
Look for strong coupling between fields and methods. In general all fields that are being used in methods must be specific to that method.
How It Manifests (Why)
Divergent Change can occur due to a poor program structure. Especially when a class is not modular enough or contains strong coupling with fields.
How to Fix It (Possible Treatments)
In Example 1, Extract Class is used. Extract Class in this case is used because of strong coupling in the expectation of the structure of a field (validation),
which causes changes when adding a new type of protocol in methods unrelated to that change.
In Example 2, Extract Class is also used. We say that the class contains multiple purposes, but ideally, each class should have
its own purpose and functionality. Therefore, we extract each functionality as a class, preventing the divergent change of one huge clas
when some change has to be made.
Other treatments are also possible based on the specific scenario, they can be found here
Examples
Example 1
Before:
If we were to add a new type of connection, we would have to make changes to both the unrelated methods getDomain() and checkValidUrl()
Observed Code Smells:
- Divergent Change (lines 9-18, 20-27)
After:
Applied Extract Class to split the behavior into two classes. Both classes have the same behavior so, created interface Connector for inheritance
Refactoring Applied:
- Divergent Change
- Extract Class (HTTPConnector, FTPConnector)
Observed Code Smells After Refactoring:
- None
Example 2
Before:
The services of Draw, Move and Resize are all inside one class which is LucyChartService.
If we add one more shape to the ShapeType Enum, the structure requires to change many spots altogether within
the LucyChart class, which constitutes a Divergent change.
Observed Code Smells:
- Divergent Change (lines 75-158)
- Switch Statements (lines 78-98, lines 103-122, lines 128-157)
After:
The services of Draw, Move and Resize should have their own classes and single responsibility.
Refactoring Applied:
- Divergent Change:
- Extract class (lines 124-153, lines 155-184, lines 186-215)
Observed Code Smells After Refactoring:
- None
When to Ignore
None
More
- Example 1
- Example 2
- Before
- After
001002 class Connector {003 private String url;004005 public Connector(String url) {006 this.url = url;007 }008009 public boolean checkValidUrl() {010 if (this.url.startsWith("http")) {011 System.out.println("Valid HTTP url");012 return true;013 } else if (this.url.startsWith("ftp")) {014 System.out.println("Valid FTP url");015 return true;016 }017 return false;018 }019020 public String getDomain() {021 String domain = "";022 if (this.url.startsWith("http")) {023 domain = this.url.substring(7, this.url.indexOf("/", 8));024 } else if(this.url.startsWith("ftp")) {025 domain = this.url.substring(this.url.indexOf("@")+1, this.url.lastIndexOf(":"));026 }027 return domain;028 }029 }030031 public class DVCHBE1 {032 public static void main(String[] args) {033 Connector serebii = new Connector("http://serebii.net/filepath");034 Connector ubuntu = new Connector("ftp://yash:1234@ubuntumachine.com:22/filepath");035 serebii.checkValidUrl();036 System.out.println(serebii.getDomain());037 ubuntu.checkValidUrl();038 System.out.println(ubuntu.getDomain());039 }040 }