Software Architecture Concepts: [ Part 8] SOLID Principles The First Principle : [S]
Have you ever tried to refactor/clean up some program code only to discover you are just dealing with a different type of mess? reordering spaghetti in a pan doesn't make it a different type of pasta. It is only by having a deep understanding of OOD principles that you are able to clean things up.
SOLID is an acronym for the first five object-oriented design (OOD) principles by Robert C. Martin (also known as Uncle Bob). en.wikipedia.org/wiki/Robert_C._Martin
SOLID is an acronym that stands for:
S - Single-responsiblity Principle O - Open-closed Principle L - Liskov Substitution Principle I - Interface Segregation Principle D - Dependency Inversion Principle
Single Responsibility Principle
Single responsibility principle states that
A component in the system should have one and only one reason to change, meaning that a component should have only one job.
A component here can be as simple as a function/class.
When you are the only one working on a code this is usually not a problem, you have written everything and you understand every bit of the logic. you know everything in the system because you have written in and you know the reasoning behind every bit of logic. You also understand why every bit of logic exist so it doesn't make a difference if all your invoice generation business logic is written inside a single method for example because when you need to change or update something you have written every thing the first time and you are aware of each business rule so you can modify and change components easily.
An example of this a component in the system that generates an invoice for your ecommerce system might look like this:
public class Invoice {
List<LineItems> line Items;
...
public Json calculateBillableAmount() {
double totalAmount;
// Calculate weight charges
...
// Calculate profit margin on line items
...
// Calculate taxes
...
// Calculate discounts and apply offers
...
return Json("totalAmount", totalAmount);
}
}
The biggest problem with the calculateBillableAmount method is that the calculateBillableAmount handles the logic to calculate multiple aspects of the invoice as well as formatting it to Json.
Consider a scenario where the output should be converted to another format like HTML, XML.
All of the logic would be handled by the Invoice class. This would violate the single-responsibility principle. The Invoice class should only be concerned with calculating the invoice. It should not care whether the user wants JSON or HTML.
Second problem is inside the calculateBillableAmount, the same function is in charge of calculating the invoice amount based on weight, profit, discounts, etc..
The truth is that unless this is a simple system/a start up prototype, multiple people are going to contribute to the system. Each one should have understanding of part of the system. If any bit of the invoice calculation needs to changed/there is the change in the required format the calculateBillableAmount will need to change, this breaks the Single Responsibility Principle.
A better breakdown of this logic that satisfy the Single Responsibility Principle might be;
class WeightChargeCalculator {
public double calculateWeightChargesOnLineItems(List<LineItem> lineItems) {
...
}
}
class TaxChargeCalculator {
public double calculateTaxChargesOnLineItems(List<LineItem> lineItems) {
...
}
}
class InvoiceDiscountsCalculator {
public double calculateDiscountOnLineItems(List<LineItem> lineItems) {
...
}
}
class ProfitMarginCalculator {
public double calculateProfitMarginOnLineItems(List<LineItem> lineItems) {
...
}
}
public class Invoice {
List<LineItems> line Items;
WeightChargeCalculator weightChargeCalculator;
TaxChargeCalculator taxChargeCalculator;
ProfitMarginCalculator profitMarginCalculator;
InvoiceDiscountsCalculator invoiceDiscountsCalculator;
public doublc calculateBillableAmount() {
double totalAmount;
// Uses separate components to calculate invoice amount
return totalAmount;
}
}
public class InvoiceJsonFormatter {
public JSON formatInvoiceAmount(double amount) {
...
}
}
Using a break down like this makes it easier to maintain & contribute to the system and doesn't break the single responsibility principle.