Design Principles and Patterns
In this post, I want to list down the important design principles that I have learned and I try to apply while designing and developing software. So what are design principles ? I wasn’t aware about design principles during my initial years in software development. I heard about design patterns though, my manager asked me to read up about different patterns on dofactory, while preparing for a client interview. I looked up that website and found a number of patterns listed out, separated into different categories. Though I read about these patterns honestly I didn’t understand much about them and what they do and why we need to use them.
Later in my career while I was working on a big program, on which I learned a lot about software design and development, I realized that all these design patterns are different ways to enforce the fundamental design principles. I will try to list out the design principles that I learnt.
Cohesion and Coupling : Programming paradigm changed from code having many jumps(“GOTO” statements ) and returns- spaghetti code – to using sub routines , block structures and while loops – Structured Programming in the 1970’s. Cohesion and Coupling are the core principles of Structured Programming
Cohesion, as the name suggests, indicates that software entities– a class or function or a module – should have closely related responsibilities. The tasks performed should closely relate to each other. A good software design should have high cohesion. Having high cohesion increases maintainability and decreases dependency (coupling)
Coupling, measures the level of dependency between two software entities. Simply said two components A and B are said to be coupled if you can’t change A without changing B. Good software design should have low coupling between components. Having low coupling increases the maintainability and reuse of software.
Separation of Concerns (SoC) : Separation of Concerns is a principle that helps achieve low coupling and high cohesion. SoC, was introduced by Edsger Dijkstra in 1974. It is the process of separating a computer program into distinct features(Concerns) that overlap in functionality as little as possible. SoC suggests focusing on one concern at a time.
Progress towards SoC is achieved using modular programming(separation of code into modules) and use of encapsulation (information hiding). Modules will have their own interfaces to communicate with other modules and hide internal implementation details.
SoC is a generic principle and different Programming Paradigms supported it in different ways. Procedural Programming (PP, expressed in C and other languages ) supported SoC using functions and procedures. Object oriented programming supported SoC using classes. However SoC is not limited to only code, the concept is applicable to many aspects of software engineering, you can apply SoC while designing a module, or while defining architecture. A number of patterns are a direct manifest of this fundamental principle
Object – Oriented Design (OOD) Principles: One major programming paradigm shift was from Procedural Programming (PP) to Object Oriented Programming. In Object Oriented programming real world entities and their interactions are represented as objects.
GangOfFour(GOF) in their book “Design Patterns: Elements of Reusable Object-Oriented Software” listed two fundamental OOD principles
1. Program to an interface, not an implementation : This principle is really about dependency relationships. For example you have a class called Customer and one of its method is UpdateAddress() and your Customer class uses Logger to log address update action. So your Customer class is dependent on Logger class.
In this implementation the class Customer is tightly coupled with Logger class. If Logger class is broken Customer class also is broken. This principle advocates using interfaces and programming to an interface. So if we use this principle the above implementation is refactored to
2. Favor Object Composition over Class Inheritance : This principle is really about code reuse. So what’s problem with Class Inheritance and why should we favor composition ? Class inheritance also results in code reuse, but with class inheritance the derived class will have visibility to parent class state and derived class is broken if parent class is broken. Also when you derive from a class you should ensure that the derived class doesn’t alter the behavior of parent class and the derived class should be used interchangeably with base class. In Object Composition, if a class needs to reuse some functionality, the class is declared and initialized using a private variable in the class.
SOLID( Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion )is an acronym, coined by Uncle Bob Martin introducing basic Object Oriented Design principles. These SOLID principles reinforce the basic OOD principles
Single Responsibility Principle (SRP) : SRP states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.
Responsibilities are axes of change. Requirement changes map to responsibility changes. If a class has more responsibilities it has more reasons to change, and it will lead to high coupling of these responsibilities.
As an example, consider a module that compiles and prints a report. Such a module can be changed for two reasons. First, the content of the report can change. Second, the format of the report can change. These two things change for very different causes; one substantive, and one cosmetic. The single responsibility principle says that these two aspects of the problem are really two separate responsibilities, and should therefore be in separate classes or modules. It would be a bad design to couple two things that change for different reasons at different times.
Open/closed principle (OCP) : OCP principle states that a software entity(function, class) should be open for extension, but closed for modification. Bertrand Meyer proposed this design principle. As per this principle classes should be conceived in such a way that they never face change – closed for modification.
So, how does one modify when a change is required ? you should add new code and not touch the old code. In practical terms OCP is achieved by implementing a fixed interface in classes that can change, callers of this class can work against the interface (remember the first basic principle ).
Liskov’s Substitution Principle (LSP): LSP states that when a class is derived from an existing one, the derived class can be used in any place where parent class is accepted. Languages which support polymorphism support LSP principle, but extra precautions should be taken while designing the base classes and using virtual methods.
Interface Segregation Principle (ISP) : ISP states that once an interface has become too ‘fat’ it needs to be split into smaller and more specific interfaces so that any clients of the interface will only know about the methods that pertain to them. In a nutshell, no client should be forced to depend on methods it does not use. In a nut shell it means break a bigger interface into smaller interfaces so that clients calling this class can be only aware of the smaller interfaces useful to them, decoupling these clients from all the other interfaces which they don’t need.
Dependency Inversion Principle (DIP): DIP states that High level modules should not depend upon low-level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. Well lets take an example to understand DIP principle. Lets say we have checkout manager and it processes payment information and updates inventory.
Now DIP states that, high level modules should not depend upon low-level modules. Both should depend upon abstractions. So PaymentProcessingManager and InventoryManager will implement interfaces and CheckoutManager will depend on these interfaces and the lower level managers depend on the interfaces. Below is the code that implements DIP principle.
And the dependency diagram will change to:
SOLID principles are different ways to achieve the basic principles, low coupling and high cohesion.
Don’t Repeat Your Self (DRY) : This is another popular principle, which simply states do not repeat things. Same functionality shouldn’t be duplicated. The principle has been formulated by Andy Hunt and Dave Thomas in their book The Pragmatic Programmer. When the DRY principle is applied successfully, a modification of any single element of a system does not require a change in other logically-unrelated elements. This is also known as single source of truth principle.
Keep it simple, Stupid!(KISS) : is a design principle articulated by Kelly Johnson.The KISS principle states that most systems work best if they are kept simple rather than made complex, therefore simplicity should be a key goal in design and unnecessary complexity should be avoided.
Design Patterns : I started of with my story of how I got introduced to design patterns and that they didn’t make much sense to me initially. Lets now see what a design pattern is : A design pattern is a known and well established core solution applicable to a family of concrete problems that might show up during implementation. So what design patterns really do is provide a solution to problems that might come up during software implementation. They cater to specific scenarios and they originate from real world experience.
What design patterns really do is show a implementation to specific problems by following the above design principles. Design Patterns should never be interpreted dogmatically. I worked with a technical lead who read about some patterns and wanted to implement them whether they are really required or not. That’s not the right way to use patterns. Usage of Design Patterns doesn’t guarantee the success of projects.
Conclusion: Design Principles, Patterns , what they really do is give some guidance on how to build good, maintainable and testable software. Remember the fundamental principles of low coupling and high cohesion and all other principles state a way to achieve this and aid in designing software. Patterns and Principles are purely from an software engineering perspective, users don’t care how many principles you have followed or how many patterns you used all users care about software is whether it helps them do their work.