Welcome to the first lesson of the "Clean Code with Multiple Classes" course! 🎉 This course is designed to help you write code that's easy to understand, maintain, and enhance. Within this scope, effective class collaboration is crucial for building well-structured applications. In this lesson, we will delve into the intricacies of class collaboration and coupling — key factors that can make or break the maintainability of your software. Specifically, we'll address some common "code smells" that indicate problems in class interactions and explore ways to resolve them.
Let's dive into the challenges of class collaboration by focusing on four common code smells:
- Feature Envy: Occurs when a method in one class is overly interested in methods or data in another class.
- Inappropriate Intimacy: Describes a situation where two classes are too closely interconnected, sharing private details.
- Message Chains: Refers to sequences of method calls across several objects, indicating a lack of clear abstraction.
- Middle Man: Exists when a class mainly delegates its behavior to another class without adding functionality.
Understanding these code smells will enable you to improve your class designs, resulting in cleaner and more maintainable code.
These code smells can significantly impact system design and maintainability. Let's consider their implications:
- They can lead to tightly coupled classes, making them difficult to modify or extend. 🔧
- Code readability decreases, as it becomes unclear which class is responsible for which functionality.
Addressing these issues often results in code that's not only easier to read but also more flexible and scalable. Tackling these problems can markedly enhance software architecture, making it more robust and adaptable.
Feature Envy occurs when a method in one class is more interested in the fields or methods of another class than its own. Here's an example:
C#1using System.Collections.Generic; 2 3class ShoppingCart 4{ 5 private List<Item> items = new List<Item>(); 6 7 public double CalculateTotalPrice() 8 { 9 double total = 0; 10 foreach (Item item in items) 11 { 12 total += item.Price * item.Quantity; 13 } 14 return total; 15 } 16} 17 18class Item 19{ 20 public double Price { get; private set; } 21 public int Quantity { get; private set; } 22}
In this scenario, CalculateTotalPrice()
in ShoppingCart
overly accesses data from Item
, indicating feature envy.
To refactor, consider moving the logic to the Item
class:
C#1using System.Collections.Generic; 2 3class ShoppingCart 4{ 5 private List<Item> items = new List<Item>(); 6 7 public double CalculateTotalPrice() 8 { 9 double total = 0; 10 foreach (Item item in items) 11 { 12 total += item.CalculateTotal(); 13 } 14 return total; 15 } 16} 17 18class Item 19{ 20 public double Price { get; private set; } 21 public int Quantity { get; private set; } 22 23 public double CalculateTotal() 24 { 25 return Price * Quantity; 26 } 27}
Now, each Item
calculates its own total, reducing dependency and distributing responsibility appropriately. ✔️
Inappropriate Intimacy occurs when a class is overly dependent on the internal details of another class. Here's an example:
C#1class Library 2{ 3 private Book book; 4 5 public void PrintBookDetails() 6 { 7 Console.WriteLine("Title: " + book.Title); 8 Console.WriteLine("Author: " + book.Author); 9 } 10} 11 12class Book 13{ 14 public string Title { get; private set; } 15 public string Author { get; private set; } 16}
In this scenario, the Library
class relies too heavily on the details of the Book
class, demonstrating inappropriate intimacy. The key difference between Inappropriate Intimacy and Feature Envy is that inappropriate intimacy involves a significant intertwining between two classes, while feature envy is about a method's excessive interest in another class's data or behavior instead of its own.
To refactor, allow the Book
class to handle its own representation:
C#1class Library 2{ 3 private Book book; 4 5 public void PrintBookDetails() 6 { 7 Console.WriteLine(book.GetDetails()); 8 } 9} 10 11class Book 12{ 13 private string Title { get; set; } 14 private string Author { get; set; } 15 16 public string GetDetails() 17 { 18 return $"Title: {Title}\nAuthor: {Author}"; 19 } 20}
This adjustment enables Book
to encapsulate its own details, encouraging better encapsulation and separation of concerns. 🛡️
Message Chains occur when classes need to traverse multiple objects to access the methods they require. Here's a demonstration:
C#1class User 2{ 3 private Address address; 4 5 public Address GetAddress() 6 { 7 return address; 8 } 9} 10 11class Address 12{ 13 private ZipCode zipCode; 14 15 public ZipCode GetZipCode() 16 { 17 return zipCode; 18 } 19} 20 21class ZipCode 22{ 23 public string GetPostalCode() 24 { 25 return "90210"; 26 } 27} 28 29// Usage 30User user = new User(); 31user.GetAddress().GetZipCode().GetPostalCode();
The chain user.GetAddress().GetZipCode().GetPostalCode()
illustrates this problem.
To simplify, encapsulate the access within methods:
C#1class User 2{ 3 private Address address; 4 5 public string GetUserPostalCode() 6 { 7 return address.GetPostalCode(); 8 } 9} 10 11class Address 12{ 13 private ZipCode zipCode; 14 15 public string GetPostalCode() 16 { 17 return zipCode.GetPostalCode(); 18 } 19} 20 21// Usage 22User user = new User(); 23user.GetUserPostalCode();
This adjustment makes the User
class responsible for retrieving its postal code, creating a clearer and more direct interface. 📬
A Middle Man problem occurs when a class primarily exists to delegate its functionalities. Here's an example:
C#1class Controller 2{ 3 private Service service; 4 5 public void Execute() 6 { 7 service.PerformAction(); 8 } 9} 10 11class Service 12{ 13 public void PerformAction() 14 { 15 // Action performed 16 } 17}
The Controller
doesn't do much beyond delegating to Service
.
To refactor, simplify delegation or reassign responsibilities:
C#1class Service 2{ 3 public void PerformAction() 4 { 5 // Action performed 6 } 7} 8 9// Usage 10Service service = new Service(); 11service.PerformAction();
By removing the unnecessary middle man, the design becomes more streamlined and efficient. 🔥
In this lesson, you've explored several code smells associated with suboptimal class collaboration and coupling, including Feature Envy, Inappropriate Intimacy, Message Chains, and Middle Man. By identifying and refactoring these smells, you can elevate your code's clarity and maintainability.
Get ready to put these concepts into practice with upcoming exercises, where you'll identify and refactor code smells, strengthening your skills. Keep striving for cleaner, more effective code! 🌟