Most Popular Design Principles (YAGNI, KISS, DRY, TDA)
Today, I’ll talk about the most popular software design principles. So what is a software design principle ?
Software design principles are a set of well-established guidelines that help developers create maintainable, scalable, and efficient software.
The main aim of is software design principle is to:
- Reduce complexity
- Improve code quality
- Guide the design process
It’s difficult to pinpoint an exact number of software design principles as they are constantly evolving and new ones emerge as the field progresses. However, there are several well-established and widely accepted principles that form the foundation of good software design.
In this article my main focus is on some most popular design principles which are YAGNI, KISS, DRY and TDA principle
Let's talk about YAGNI(You Aren’t Gonna Need It)
The YAGNI principle, which stands for “You Aren’t Gonna Need It,” is a software development principle that emphasizes implementing functionality only when it’s truly necessary. This means avoiding the temptation to add features or functionalities that we think we might need in the future, but haven’t encountered a concrete requirement for yet.
Here is a simple code example of YAGNI
CASE-I: We’re building a simple shopping cart application. Initially, the requirement is to simply add items to the cart and calculate the total price.
public class ShoppingCart
{
private List<Product> items = new List<Product>();
public void AddItem(Product product)
{
items.Add(product);
}
public double GetTotalPrice()
{
return items.Sum(item => item.Price);
}
}
This code implements the core functionality of adding items and calculating the total price. It doesn’t include features like removing items, applying discounts, or handling different types of products. These features are not currently required and can be added later if the need arises.
Advantages of YAGNI:
- As Simpler Codebase: The code becomes cleaner and easier to understand by avoiding unnecessary complexity.
- Reduce Development Cost & Time: By focusing on immediate needs, we avoid spending extra time building features that might not be used.
- Increased flexibility: This principle makes it easier to adapt the code to future requirements if the base code is simple and focused.
It’s important to note that YAGNI does not advocate for neglecting future considerations entirely. It’s still essential to design your code with potential future needs in mind, but without prematurely implementing functionalities that are not currently required.
Now another popular principle is KISS (Keep It Simple, Stupid)
The KISS principle, which stands for “Keep It Simple, Stupid,” emphasizes writing code that is clear, concise, and easy to understand. This doesn’t necessarily mean writing the shortest code possible, but rather focusing on
- More Readability: We use clear variable and method names, and proper indentation for better visual flow.
- Much Simplicity: we might break down complex tasks into smaller, well-defined functions promoting modularity.
- Concise Maintainability: we could write code that is easy to understand and modify for ourselves and others.
Here is a Complex approach example of the KISS principle:
if (number % 2 == 0)
{
Console.WriteLine("The number is even.");
}
else if (number % 3 == 0)
{
Console.WriteLine("The number is divisible by 3.");
}
else
{
Console.WriteLine("The number is neither even nor divisible by 3.");
}
Here is a Simple approach example of the KISS principle:
void AnalyzeNumber(int number)
{
if (number % 2 == 0)
{
Console.WriteLine("Even");
return; // Exit function if even
}
if (number % 3 == 0)
{
Console.WriteLine("Divisible by 3");
}
else
{
Console.WriteLine("Neither even nor divisible by 3");
}
}
int userNumber = int.Parse(Console.ReadLine());
AnalyzeNumber(userNumber);
By separating the logic into a function with clear naming and using an early return
statement, the code becomes more readable and easier to maintain, especially when dealing with more complex scenarios.
Please note that KISS encourages finding the balance between functionality and simplicity. Always strive for code that is both effective and understandable.
The other design principle is DRY(Don’t Repeat Yourself) isn’t fancy
The DRY principle, which stands for “Don’t Repeat Yourself,” emphasizes avoiding code duplication. This means any piece of logic or functionality should be implemented only once and then reused wherever needed.
Advantages of DRY:
- Reduced code size: Less code to write and maintain.
- Increased consistency: Consistent behavior across code sections.
- Easier maintenance: Changes in one place affect all usages.
Here is a simple example of DRY:
public class ShapeAreaCalculator
{
public double CalculateCircleArea(double radius)
{
return Math.PI * Math.Pow(radius, 2);
}
public double CalculateSquareArea(double side)
{
return side * side;
}
//DRY implementation using a single method with different parameters
public double CalculateArea(string shapeType, double parameter)
{
if (shapeType == "circle")
{
return Math.PI * Math.Pow(parameter, 2);
}
else if (shapeType == "square")
{
return parameter * parameter;
}
else
{
throw new ArgumentException("Unsupported shape type");
}
}
}
In this example, the initial code duplicates the area calculation logic for circles and squares. The DRY implementation uses a single CalculateArea
method with different parameters depending on the shape type, promoting code reusability and reducing redundancy.
Finally, The most interesting design principle is TDA which stands for
Tell, Don’t Ask Principle
The Tell, Don’t Ask (TDA) principle suggests that, when interacting with objects, it’s often better to instruct them to perform actions rather than query their state and then act based on them. Here advantages:
- Encapsulation: Keeps data and logic encapsulated within objects, promoting better data protection and object autonomy.
- Loose Coupling: Reduces dependencies between objects, making code more modular and easier to maintain.
- Readability: Makes code more explicit about the intended behavior.
Here Avoid the Asking approach Example:
public class Order
{
private bool isPaid;
public bool IsReadyToShip()
{
if (isPaid && !IsShipped)
{
return true;
}
return false;
}
}
Here the code retrieves the order’s isPaid
state and checks another property (IsShipped
, not shown) to determine if it's ready to ship.
Now Telling approach Example:
public class Order
{
private bool isPaid;
public void MarkPaid()
{
isPaid = true;
}
public bool ShipIfReady()
{
if (isPaid)
{
Ship(); // Call the shipping method directly
return true;
}
return false;
}
private void Ship()
{
// Implement actual shipping logic
}
}
Here, the MarkPaid
method explicitly tells the object to update its internal state. The ShipIfReady
method checks the state and, if paid, calls the Ship
method to perform the actual shipping logic. This approach keeps the shipping logic encapsulated within the Order
class and avoids exposing its internal state directly.
** NB: The given design principles are a guideline, not a strict rule. Use your judgment and consider the specific context of your project when deciding how to best avoid code duplication, deciding between asking or telling objects to perform actions.
Thank you ):