Mieke Gerretzen said “Good design goes to heaven, bad design goes nowhere”. The importance of a good design cannot be stressed enough be it in architecture or software development. Robert Martin pointed out that 3 basic characteristics of a bad software design are rigidity, fragility and immobility. Component design principles discussed below are guidelines to ensure a better designed code.
Open Close Principle
“A module should be open for extension but closed for modification”.
Changes have to be accommodated after a piece of code is written. These changes should be made in such a way that minimum ripple effect is felt among the related classes. The changing functionality should be coded to an interface from which the new classes can derive the common functionality and introduce the change. In the example, the account class is closed to changes, but if we want to add a new account of say recurring deposit type we have to inherit from the AccountType which acts as an abstraction interface and so avoids changes in Account
Dependency Inversion Principle
“Depend on abstractions, do not depend on concretions”
In any design there are low level classes that implement functionality and higher level classes that control the logic. People tend to first code the functionality first at the implementation level and they design classes to use the functionality. This is a bad practice as, if changes need to be made to the low level classes which is often the case the higher classes also have to be altered. Consider the example of a copy module that reads from the keyboard and writes to the printer. People tend to write classes such as keyboard read and printer write(low level classes) first and then write the copy class to use these classes. But when a common change comes along such as changing the output to be written to a file, we will have to change the copy class as well. Instead a good practice would be to use abstractions and make the low level classes to depend on these abstractions. So the design order would be High level classes->Abstraction Layer->Low level classes.
Interface Segregation Principle
“Many client specific interfaces are better than one general purpose interface”
We should create a specialized interface to serve each major category of clients. We should not create fat or polluted interfaces. Fat interfaces force the submodule to implement dummy methods. Consider the example of a company which employs robots and human workers. A human works and eats, whereas a robot only works. If work and eat are in a single interface, robot had to implement a dummy method for eat. A better solution would be to create 2 separate interfaces one for work and another for eat.
Single Responsibility Principle
“A class should have one and only one reason to change”
If a class has 2 reasons to change, then the class must be split into 2 classes else changing one responsibility will change another functionaity. Consider a Email example- Say it has 2 potential changes 1) content (to html) 2) protocol used (to POP3). If we keep only one class, each change for a responsibility might affect the other one as follows creating a undesirable effect:
- Adding a new protocol will create the need to add code for parsing and serializing the content for each type of field.
- Adding a new content type (like html) make us to add code for each protocol implemented.
Liskov’s Substitution Principle
“Subclasses should be substitutable for their base classes “
If a component is working well with a base class, it should work just as well with the its subclass passed to it. In other words the pre and post condition must be the same regardless of whether a base class or sub class is passed the component.