“Good artists copy; great artists steal”
They say imitation is the sincerest form of not being stupid, and if you think about it, that just may be one of the truest phrases you’ve heard in a long time. While coming up with an original idea on your own is pretty satisfactory, there really isn’t much need for a new idea if someone already solved the problem you’re trying to solve. That’s why when you’re trying to come up with a design, it’s probably not such a bad idea to look somewhere else for ‘inspiration’, after all, it’s not a contest to see who comes up with the best idea, but who does it faster, and smarter.
Design principles are basic tools and/or techniques that can be applied while trying to design something. In software, the purpose of design principles is to make our code more easily maintainable, more flexible, and more extensible.
If you’ve read something about Object Oriented Programming, then you might already know some of its principles: encapsulating what varies, coding to an interface rather than a concrete implementation from the very beginning, having as little reasons to change as possible, and giving each class a purpose, behaviour, and function. Now, let’s look at some design principles.
The open-closed principle
This is all about allowing change without having the need to modify existing code. That may sound contradictory, but what we really want is to be open for extension, while being closed for modification.
Being open for extension means allowing the existence of subclasses that can inherit, and perhaps modify a certain behaviour. This allows to do a certain something another way, while leaving previous code alone, since it already works for a number of previous cases. You simply override a method and write a new way to get something done.
Being closed for modification means that while we want to allow change, we really don’t want to mess around with already-existing, already-working code. The reason for this is that while you or somebody else might think that they’ve got another solution to performing an action, nothing really ensures that it will work every single time, and since your previous code already works perfectly in most cases, you probably don’t want to risk it.
The OCP is all about flexibility, and that means going beyond just inheritance. While it’s true that inheriting behaviours from a superclass is a simple way to be able to modify behaviours without needing to change existing code, there’s way more to it than just overriding existing methods.
The don’t repeat yourself principle
This principle, while simple, often proves to be critical when trying to write code that’s easy to maintain and reuse later on. We want to avoid duplicate code whenever possible. For this we want to follow some simple steps:
- Abstracting and removing the common code: Every time you find code that’s duplicated, you’ll probably want to take it away and put it in a single place (wherever it seems like it makes the most sense).
- Referencing the code from step 1: Now that you’ve removed duplicate code, you still need that code for your program to work properly. For this, we simply make a reference to our code now that we’ve put it away somewhere else where it makes sense to have it.
This is all about having one requirement/function in one place. When applying object-oriented programming principles to our design, we must always keep in mind that each object is good at doing one thing alone, so there’s no reason to have multiple things performing various tasks that they were not designed for.
The Single Responsibility Principle
Again, we’re referencing object-oriented programming principles here. The SRP principle is all about identifying who does what. You want each specific object that you design to have just one specific responsibility; and to be really good at it. The reason for this is to know where to look when you want to modify something. Having each object perform just one task makes it easy to know where to look for when trying to make changes to your code.
You know that you’ve done a great job applying the SRP principle when your each of your objects has one, and only one reason to change, if any at all. If you find that an object has multiple responsibilities that it shouldn’t have, don’t hesitate on creating new objects for the sole purpose of having them perform a specific task.
The Liskov Substitution Principle
Subtypes must be sustitutable for their base types. This means that we want well-designed inheritance, which refers to the fact that when you inherit from a base class, you should be able to easily substitute your subclass in order to fit our base class’ needs without having everything crash. If everything goes to hell when trying to modify a subtype, that means you’re doing something wrong.
The great thing about LSP is that it helps reveal hidden problems with our inheritance structures. At first glancec it may seem like our subclasses using a certain inherited behaviour are a good idea, but once we look closer, we may find some problems with our implementations. If you refuse to follow the Liskov Substitution principle, you might find some really confusing code later on. While it may not seem like a big deal in the beginning, when you arrive at the debugging stage, you’ll find that it’s a nightmare to debug.