Code Smell — Practical Guide using .Net Core Part-III

Code Smell — Data Clumps, Shotgun Surgery, Lazy Class and Speculative Generality
In first part of story Code Smell — Practical Guide using .Net Core Part-I we talked about about the Code Smell, identification and remedies and enlisted the most prevalent code smells. And in following story “Code Smell — Practical Guide using .Net Core Part-II” we covered Inconsistent Naming, Too Many Comments and Switch Statements
In this story we’ll cover Data Clumps, Shotgun Surgery, Lazy Class and Speculative Generality
9. Data Clumps
Data Clumps occur when the same group of parameters or variables is consistently passed around or used together across multiple parts of the code. This repetition suggests that these pieces of data are related and may benefit from being encapsulated together.
Example
Let’s consider a scenario where information about a person’s address is scattered across various methods or classes:
class PersonDetails
{
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
class OrderProcessing
{
public void ProcessOrder(string street, string city, string state, string zipCode)
{
// processing logic
}
public void ValidateAddress(string street, string city, string state, string zipCode)
{
// validation logic
}
}
Solution
To address the Data Clumps code smell, you can encapsulate related data into a separate class or structure. In C#, you might create a class to represent the address:
class Address
{
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
class OrderProcessing
{
public void ProcessOrder(Address address)
{
// processing logic using address
}
public void ValidateAddress(Address address)
{
// validation logic using address
}
}
10. Shotgun Surgery
Shotgun Surgery is a code smell that occurs when a single change in the codebase requires multiple modifications across different classes or modules. In C#, this may manifest as tightly coupled dependencies where a modification in one class forces changes in several other classes.
Example
Consider a scenario where a change in a data model requires updates in multiple classes that handle data manipulation, validation, and presentation. If altering a property in the data model leads to modifications in various classes throughout the codebase, it indicates Shotgun Surgery.
// Original Data Model
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
// Class handling data manipulation
public class DataManager
{
public void ProcessUserData(User user)
{
// Process data
}
}
// Class responsible for data validation
public class Validator
{
public bool ValidateUserAge(User user)
{
// Validation logic
}
}
// Presentation layer
public class UserPresenter
{
public void DisplayUserInfo(User user)
{
// Display logic
}
}
If a change in the User
data model (e.g., adding a new property) requires modifications in DataManager
, Validator
, and UserPresenter
, it signifies Shotgun Surgery.
Solution
To address Shotgun Surgery, introduce an abstraction layer or design patterns that decouple the components. In C#, you can use interfaces, abstract classes, or dependency injection to minimize dependencies between classes.
public interface IUserProcessor
{
void ProcessUserData(User user);
}
public interface IUserValidator
{
bool ValidateUserAge(User user);
}
public interface IUserDisplay
{
void DisplayUserInfo(User user);
}
public class DataManager : IUserProcessor
{
public void ProcessUserData(User user)
{
// Process data
}
}
public class Validator : IUserValidator
{
public bool ValidateUserAge(User user)
{
// Validation logic
}
}
public class UserPresenter : IUserDisplay
{
public void DisplayUserInfo(User user)
{
// Display logic
}
}
By introducing interfaces, each class depends on abstractions rather than concrete implementations, reducing the impact of changes and mitigating the Shotgun Surgery code smell.
11. Lazy Class
A Lazy Class is a class that doesn’t carry its weight in terms of functionality or responsibilities. It typically contains only a few methods or properties, making its existence questionable. In other words, it lacks a clear purpose or meaningful contribution to the overall functionality of the system.
Example
Consider the following example:
public class SimpleLogger
{
public void LogInfo(string message)
{
Console.WriteLine($"INFO: {message}");
}
}
public class MainApplication
{
private SimpleLogger logger;
public MainApplication()
{
this.logger = new SimpleLogger();
}
// Other methods...
public void DoSomething()
{
// Some important functionality...
logger.LogInfo("Something happened."); // Using the lazy class
}
}
In this example, the SimpleLogger
class is a potential Lazy Class because it only has a single method for logging information. It doesn't contribute significantly to the overall functionality of the application.
Solution
1. Inline or Merge
Consider moving the functionality directly into the calling class or merging it with another class if the functionality is closely related.
public class MainApplication
{
// Other methods...
public void DoSomething()
{
// Some important functionality...
LogInfo("Something happened."); // Inline the logging functionality
}
private void LogInfo(string message)
{
Console.WriteLine($"INFO: {message}");
}
}
2. Eliminate
If the class doesn’t provide substantial value, consider removing it altogether and incorporating its methods directly into the calling class.
public class MainApplication
{
// Other methods...
public void DoSomething()
{
// Some important functionality...
LogInfo("Something happened."); // Directly include the logging functionality
}
private void LogInfo(string message)
{
Console.WriteLine($"INFO: {message}");
}
}
By addressing Lazy Classes, you can streamline your codebase, improve its clarity, and eliminate unnecessary abstractions that don’t contribute meaningfully to the application.
12. Speculative Generality
Speculative Generality in a class refers to the presence of features or functionality in the code that are not currently needed or used, but have been added in anticipation of potential future requirements. This can lead to unnecessary complexity, decreased code readability, and increased maintenance efforts. In C#, examples of speculative generality in a class might include unused methods, properties, or generic types that were added without a clear current use case.
Example
- Presence of methods or properties that are not utilized by the current functionality.
- Inclusion of generic types or parameters without a specific and immediate need.
- Unused interfaces or abstract classes that don’t contribute to the current functionality.
Solution
- Remove Unused Code: Identify and remove methods, properties, or types that are not currently being used.
- Refactor Generics: If generic types or parameters are not immediately necessary, consider removing them until a clear need arises.
- Focus on Immediate Requirements: Develop code based on existing and immediate requirements rather than trying to anticipate future needs.
Updated code
public class RefactoredExample
{
// Only include necessary methods
public void UsedMethod()
{
// Code that is currently used
}
}
By addressing speculative generality, you keep your codebase clean, improve its maintainability, and avoid unnecessary complexity introduced by features that are not currently serving a purpose.
Conclusion
In this final part of our series, we explored Data Clumps, Shotgun Surgery, Lazy Class, and Speculative Generality, which are common challenges in maintaining clean and efficient codebases. Addressing these issues can significantly improve the readability, maintainability, and scalability of your software.
Data Clumps occur when related data is scattered across methods or classes, leading to redundancy and poor cohesion. By encapsulating related fields into a dedicated class, you can enhance clarity and reusability.
Shotgun Surgery arises when a single change necessitates updates across multiple components. The solution lies in decoupling dependencies through interfaces, abstraction, or design patterns, minimizing the ripple effect of changes.
Lazy Class refers to underutilized classes that add unnecessary complexity. These can often be inlined, merged, or eliminated, reducing clutter and focusing functionality where it belongs.
Speculative Generality happens when features or abstractions are added prematurely, anticipating future needs. Removing unused or unnecessary elements and refactoring code to meet current requirements helps maintain simplicity and focus.
This story is a part of three parts as
Code Smell — Practical Guide using .Net Core Part-I
Code Smell — Practical Guide using .Net Core Part-II
Code Smell — Practical Guide using .Net Core Part-III