If you are in software development and working as senior programmer or application architect then you should keep these software deigning principle in your mind while working on software or application development.
Why Software Design Principles?
Software Design principles are set of guidelines to handle complexity and reduce effort needed to develop a good system design. Design principles ease problems of future development, enhancement, maintenance and also reduce the scope of error during design.
What are Software Design Principles
These are the key software design principles but most important is SOLID (top 5) which use in all software designing.
- Single Responsibility Principle : One class should do one thing and do it well.
- Open Close Design Principle : Open for extension, close for modification.
- Liscov Substitution Principle : Sub type must be substitute for super type.
- Interface Segregation Principle: avoid monolithic interface , reduce pain on client side.
- Dependency Inversion Principle: Don’t ask lets framework give it to you.
- DRY(Don’t Repeat Yourself) : avoid duplication in code.
- KISS (Keep it Simple, Stupid) : Keep you code simple, small and understandable.
- Encapsulate what changes : hides implementation detail helps in maintenance.
- Favor Composition over inheritance: Code reuse without cost of inflexibility.
- Programming for interface : Helps in maintenance, improve flexibility.
- Delegation Principle: Don’t do all things by your self. Lets Delegate it.
- YAGNI ( You Ain’t Gonna Need It) : If it’s not in the concept, it’s not in the code
All these software design principles are mainly based on four factors:
- Problem Partitioning : Divide the problem in manageable pieces.
- Modularity : Divide software to well-defined separate module .
- Abstraction: Provide component as interface and hide internal implementation.
- Strategy Of Design: Top-Down/Bottom-Up Approach
In this further section will discuss about in these principles in detail.
1 : Single Responsibility Principle (SRP)
A class should have only one reason to change. If there is any other reason create another class.
This principle is completely based on Coupling and Cohesion. This principle states that In your software design a classes should in such a way that each class should have a single purpose/responsibility/functionality.
While designing software if you put more than one functionality in single class then increase coupling between functionalities. If change required in one functionality there are chances to broke other functionality and required more testing to avoid such surprises in production environment.
Suppose you are having JPA classes as SavingAccountRepository.java and CurrentAccountRepository.java then SavingAccountRepository.java class should have only methods and queries related with Saving Accounts. It means your class should specialize in single purpose.
Others most common example of responsibilities:
- Error Handling
- Class Section/ Instantiation etc.
- This principle make your software easier to implement and prevent unexpected side-effects of future changes.
- Your class will change only if anything will change in respected responsibility.
- Need to update dependencies and compile when some respected dependencies change.
- Reduce coupling between software and components.
The Single Responsibility Principle (SRP) also provides other benefits with classes, components and micro services with single responsibility make your code easier to explain, understand, implement. It also improves development speed and easier to track bugs.
2 : Open Close Principle (OCP)
Software entities like classes, modules and functions should be open for extension (new functionality) and closed for modification.
This principle is based on inheritance or composition design pattern like Strategy Design pattern. As per this principle:
- “Open” means , Your code should be able to extend existing code in order to introduce new functionality.
- “Close” means, Once your module has been developed and tested, the code will change only when correct bugs.
For Example: A BankAccount.java base class contains all basic transaction related properties and methods. The same class can be extended by SavingAccount.java and CurrentAccount.java to handle saving and current account functionalities. If new functionalities need to add then only modification required in BankAccount.java class. Hence this class is open for extension and close for modification.
- The main benefit of Open/Close Design principle is that already developed and tested code will not modified and don’t break.
3 : Liscov Substitution Principle (LSP)
Drived type must be completely substitute of their base type.
Liscov Substitution Principle is closely related to the Single Responsibility Principle and Interface Segregation Principle.
This principle states that Subclasses or derived classes should be completely substituted of superclass. The Subclass should enhance the functionality but not reduce it.
In other words, functions that use pointers and reference of base classes must be able to use objects derived classes without knowing it.
Suppose Employee class extends Person Class by inheritance. In this way wherever you are using person class should also be able to use Employee class because Employee is a subclass of Person class.
- If this principle violates then so much extra conditional code of type checking and duplicate code need to write throughout the application that can cause bugs when the application grows.
- In methods or functions which use the superclass type must work with the object of subclass without any issue.
4 : Interface Segregation Principle (ISP)
Clients should not be forced to depend on methods in interfaces that they don’t use.
This principle states that a client should not implement an interface if it doesn’t use that. An interface should belong to clients , not to library or hierarchy and keep only those methods as required for client.
In an application service interface exposed to client should have only those methods that are related to client.
- If you violate this principle and add some more methods in interface that are not used with client then if you change any thing in interface definitely some functionality of client will break.
5: Dependency Inversion Principle (DIP)
High level module should not depend on low level module , both should depend on abstractions. Abstraction should not depend on detail. detail should depend on abstraction.
This principle provide loose coupling between the dependencies of modules and classes.
The Dependency Inversion Principle states that:
- High level modules should not depend on low level modules directly, both should depend on abstractions.
- This abstraction should not depend on details, details should depend on abstractions.
The best example of Dependency Inversion Principle is Spring framework where dependencies are injected through XML or Annotation which provide abstractions and can change with out modification on implementation.
- If you violate this principle your code will be tightly coupled because every high level module is directly referencing to lower level modules.
- Makes testing easy by injecting the mock objects with out modification on existing functionality.
6: Don’t Repeat Yourself (DRY)
Avoid duplication of code.
This principle states that don’t write duplicate code. If you have same code on multiple places then consider making a separate method. or If you are using any hard code value more than one time make this value as public static final constant.
Suppose, you are using some format to validate orderId and your complete system is based on it. In future, if your orderId format get change and if your code is duplicate then you have to modify in all places otherwise system will fail. In such scenarios better write hardcoded values and common code in methods so that make system maintenance easy.
- The main benefit of DRY principle is maintenance of system.
- This helps to design a system scalable, maintainable and reusable.
- Common functionality or utility methods will on one place.
7 : Keep it Simple, Stupid (KISS)
Keep it short, simple and Understandable.
KISS principle states that always try to keep your code in small pieces, simple and avoid unnecessary complexity.
- KISS principle helps to write easy maintainable code.
DRY and KISS principle are almost similar because it focus on small piece of code but difference is KISS principle more focused on simplicity and avoid complexity.
8 : Encapsulate What Changes
Hidden implementation detail helps in maintenance.
This principle states that encapsulate the code you expect or suspect to be changed in future. By default make your variables and methods private and increase access step by step like from private to protected not directly public.
In java lots of design patterns use encapsulated code , like Factory Design Pattern is one example where object creation code is inside factory class and in future need to enhance functionality for any new product then do it without impact of existing code.
- It’s easy to test and maintain proper encapsulated code.
9: Favor Composition Over Inheritance
Code reuse without cost of inflexibility.
In OOPS there are two ways to reuse the code, Inheritance and composition, both the ways have own advantage and disadvantage.
This principle states that, If possible always prefer composition over inheritance because composition are lot more flexible than inheritance.
- Composition allow to change behavior of class at runtime by setting properties during runtime and by using interfaces to compose a class by runtime polymorphism provides flexibility to use better implementation any time.
10 : Programming for Interface
This principle states that we should always program for interface not for implementation. It makes code flexible with any implementation of the interface.
In other words, We should always use interface type in variable, methods arguments and return type of methods like Super class type to object instead of sub class.
Programming for Interface Example
- It provides flexibility to maintenance code and change implementation.
11 : Delegation Principle
Don’t do all things by yourself, Lets delegate it
This principle states that don’t all things in yourself, we should write logic to delegate it to respective class.
- Event delegation, where an event is delegate to handlers class for handling.
- equals() and hashcode() methods compare two objects for equality. Here we ask class inself to do comparison instead of Client class doing that check.
- No duplication of code.
- Pretty easy to modify behaviour.
12 : You Ain’t Gonna Need It (YAGNI)
If it’s not in the concept, Not in the code.
YAGNI principle state that we should implement things when we need then never implement things before need them.
- If we violate this principle and write unnecessary code with perception like may required in future then unnecessarily have to take care while maintenance or some change happen.