"Don't talk to strangers" also applies to software development.
You hear from best programmers about having "loosely coupled" classes. But what does this mean?
Loose coupling means that different system parts depend on each other as little as possible. This allows each component to function independently and makes the overall system easier to maintain and extend.
When classes and modules are tightly coupled, a change in one component often requires changes in others, making the system fragile and difficult to maintain.
In contrast, loosely coupled systems isolate changes, reducing their impact on the entire system and improving modularity and testability.
In simple terms, the amount of coupling refers to how often changes in class A force changes in class B.
Tight coupling means the two classes often change together.
Loose coupling means they are mostly independent.
We even have a law for this, the "Law of Demeter (LoD) or principle of least knowledge.”
Formulated in 1987, this Law advises developers to design classes that only interact with their immediate "friends" and avoid unnecessary dependencies on distant components.
How do you translate this Law to your class design?
An object should only call methods of the objects it creates.
It can call methods on objects passed to it as parameters.
It can call methods on any direct component object.
Here are three practical strategies to Avoid Bad Coupling:
Single Responsibility Principle (SRP)
Ensure each class or module has a single, well-defined responsibility. This keeps changes isolated and minimizes the ripple effect across the system.
Use Interfaces and Abstract Classes
Define clear contracts through interfaces or abstract classes, allowing flexible and interchangeable components without tight binding to implementations.
Dependency Injection
Inject dependencies into a class rather than hard-coding them. This promotes flexibility and easier testing.
Let's review an example exploring the Single Responsibility Principle together.
Imagine we have three classes:
OrderService
Order
OrderItem
We need to calculate the Order's total
A bad implementation will be:
In this example, the OrderService class directly accesses the properties of Order and OrderItem, violating the Law of Demeter and creating tight coupling between the classes.
public class OrderItem
{
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class Order
{
public List<OrderItem> Items { get; set; }
public Order()
{
Items = new List<OrderItem>();
}
}
public class OrderService
{
public decimal CalculateTotal(Order order)
{
decimal total = 0;
foreach (var item in order.Items)
{
total += item.Price * item.Quantity;
}
return total;
}
}
A good implementation will look like:
In this improved example, the Order class encapsulates the logic for calculating the total.
The OrderService class simply calls a method on the Order object, adhering to the Law of Demeter and reducing coupling.
public class OrderItem
{
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class Order
{
public List<OrderItem> Items { get; set; }
public Order()
{
Items = new List<OrderItem>();
}
public decimal CalculateTotal()
{
decimal total = 0;
foreach (var item in Items)
{
total += item.Price * item.Quantity;
}
return total;
}
}
public class OrderService
{
public decimal GetOrderTotal(Order order)
{
return order.CalculateTotal();
}
}
We can even take this beyond and encapsulate the Item's final price within the Item class.
I like to keep it at the Order level because other factors, like discounts, can often affect the calculation and aren't below the Item class.
Key Takeaways
Reduced Coupling: Minimize dependencies between classes to ensure changes in one area don't ripple through others.
Better Modularity: Maintain isolated, well-defined modules to improve understanding and maintenance.
Easier Testing: Reduce dependencies to simplify testing, making units more self-contained and predictable.
Each unit should only talk to friends; don't talk to strangers!
System Design Classroom is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.
Articles I enjoyed this week
How Meta Achieves 99.99999999% Cache Consistency by Neo Kim
How Reddit Serves 100K Metadata Requests Per Second by Saurabh Dashora
How to Answer a LLD Interview Problem by
My experience implementing software with Pair Review by
Thank you for reading System Design Classroom. If you like this post, Share it with your friends!
Great tips Raul and some fine examples.
Also, thanks for the shoutout!
Great article, Raul. Loved the examples.