Are design principles really required while developing a software? Is it worth spending time on it instead of just focusing on making the code work?
Today I would like to talk about design principles, what are these? why do we need it? and some examples of them. Because I believe it is highly underrated while building a software or even during code reviews.
Now you might think that we have built some amazing programs, which works flawlessly in production and we did not waste time on following any design principles.
And you are probably right, but guess what? such programs are not going to last long. Most of the software we build will be used for a long period of time and as the days goes by, the same software is going to evolve with new features every week, if not every day. Moreover, there are going to be multiple developers working on the same software and everyone has different styles of coding. So if the code is not maintainable, even a small change in code will have a ripple-effect elsewhere and it will be very hard to reuse any of it and fixing even a small bug will be expensive.
So how do we make sure that our software is scalable and less prone to bugs in long run?
Well, first and foremost step is to make our code robust, reusable and chances of failure to be reduced. This can be done by giving importance to the quality of code and not just to functionality or output of the code. However, at the speed at which new version software are being released, it becomes a tad bit challenging to spend time on code quality.
Since not every developer needs to break their head to find new ways or standards which would best fit their software, there are already some treasured design principles out their, which are mostly suited for all types of software.
Be noted that these design principles are not the actual implementation for the system, instead, it provides you a way to achieve quality code for your implementation which will help in the long run for,
- Cost efficient
- And most important, easily changeable, because the only constant in the tech industry is, you know what I'm going to say next, the CHANGE.
Now, there are multiple principles which you can choose from such as,
DRY - Don't repeat yourself
KISS - Keep it simple, stupid
YAGNI - You ain't gonna need it
We will be discussing SOLID principle, which is my favorite and covers almost everything you would ever need to use. Here is the description of the principle with some examples,
S: Single responsibility ~ "A class should have only one reason to change. If there is any other reason create another class."
While the definition is quite clear, it means that a class should have only one job. This principle focuses on coupling and cohesion. The classes should be very loosely coupled, whereas the relations inside a class should be tightly coupled.
Eg. class Order, should deal with only the orders of an e-commerce site and not include billing functionality in it.
O: Open-Closed Principle ~ "Entities like classes, modules, functions or objects should be open for extension but closed for modification."
Once a class/function is built, it should only be "open" for fixing bugs or adding new functionality and "closed" for anything else.
L: Liskov substitution states that "Derived class must be able to substitute its base class"
When we have a class inherited from a base class, the inherited class can be used instead of its base class by any pointer or reference.
Eg. If Customer class is derived from User class, then wherever we use User class, we should be able to use Customer class too.
I: Interface segregation "Clients should not be forced to depend on methods in interfaces that they don’t use."
This principle states that the client should not have to have a functionality in it which it does not use, but for some reason it still needs to be present in it.
Eg. class Person is an abstract class which has methods such as, show_subjects, enroll_lectures, get_job, get_salary. If a class Employee is implementing the class Person, it will have to implement show_subjects and enroll_lectures even if it does not need it. Hence, class Person needs to be separated into two interfaces , one will have methods useful to Students and other to Employees.
D: Dependency inversion states that "the high-level modules should not depend on low-level modules, it should rather depend on abstraction"
This helps in reducing coupling between classes by adding an abstraction layer.
Eg. class Teacher defining the methods of adding new lectures into the courses,
class Teacher(): def __init__(self): self.english =  self.spanish =  def addEnglishStd(self,engStd): self.english.append(engstd) def addSpanishStd(self,spanStd): self.spanish.append(spanStd) class English(object): def __init__(self): print "english student enrolled" class Spanish(object): def __init__(self): print "spanish student enrolled"
With this approach, if another language to be added under a teacher, we have to make changes to complete Teacher class, which is against dependency inversion principle. To avoid this we can create a class Language,
class Language(): def write(): pass def speak(): pass class Teacher(): def __init__(self): self.languages =  def addLanguage(self, language): self.languages.append(language) class English(Language): def speak(self): print "Student to learn speaking english" def write(self): print "Student to learn writing english" class Spanish(object): def speak(self): print "Student to learn speaking spanish" def write(self): print "Student to learn writing spanish"
Now if any new language can be added to Teacher without having to change the Teacher class.
Python provides a module called “abc” for Abstract Base Class, which can be explored for creating such interfaces.
Hope you gained some insights on design principles and would like to spend some time on it in your upcoming project, more articles on system design coming soon in this space.
If you want to support my work, https://www.buymeacoffee.com/manishanaidu