Back again, with another off-the-top shelf; Software principles to recite before bed - literally.
Throughout the software engineering lifecycle, practices have come and gone. Those that have stuck and those that have withstood the test of time - the building blocks of what we have today. They've pushed teams and developers alike to better codebases and practices. Here, we discuss a few of the most notable.
YAGNI (You Ain't Gonna Need It)
An echo off the last article, where we discussed the endless stream of libraries , and our constant want to know them all. Are you going to use it really? Another dead end , eyy? The libraries you are actually going to use are those that you stumble upon while looking for a specific need and problem to solve, not those that you blindly go online and scroll for.
Not a state of matter. Coined by Robert C. Martin, SOLID is a software engineering acronym, broken down to:
S - Single-responsibility Principle
O - Open-closed Principle
L - Liskov Substitution Principle
I - Interface Segregation Principle
D - Dependency Inversion Principle
The goal of this is to make whatever project you, as the engineer, are working on, be maintainable and extendable. Elaborate this.
A class or module should have one and only one function.
For instance, we have a class that renders a GUI application, for instance, a PyQt program. Lots of things happening within this. Our program, in this case, streams our favourite music streams from source ABC, a video player of sorts. What about creating just one class for this? Seems simple enough, right? Well, no.
Beyond that corner of 'the program worked! ' lies a wet mop willing and ready to slap you in the face.
As with any other program, we expect downtime, a ' stream not found ' error, and additional functionality to be added to make it pomp out. You get this gist. So break it down. Our single class cannot have all this all at once, make modules to work on error logging, make modules specific for the video player, a class-specific to listing our feed and another to show history. If it can be small, make it smaller. Just do not overdo it and clear out the whole point of it.
Objects or entities should be open for extension but closed for modification.
A function, class or module can be extended, but not modified by an external entity. Within this function, method or module, include items that are mandatory for controlling the, but none of the optional methods which would limit the flexibility of the implementations.
" Good Architecture maximizes the number of decisions not made " - Robert C. Martin
All notification services show a, well, notification, but do all of them show an error notification? Break it off. Utilize polymorphism. All my error service needs to do is pass ABC to XYZ. The rest should be none of its concern. Pass it relevant info and let the rest be handled. This same principle would be extended for the success or information notification services.
Liskov Substitution Principle
Derived classes must be substitutable for their base classes.
Objects in a program should be replaceable with instances of their subtypes, without changing the correctness of the program.
Let's simplify this: what we mean is if S is a T subtype, then Type T objects can be replaced with Type S objects.
Bear with me; A school application.
We have a class
Staff that houses all the staff of an institution. As well, we have two subclasses, that is,
Support staff, both of whom are still staff members.
As usual, we have our assumptions as to what properties and methods belong to what object. Following the same principle, we state the below:
- Do not enforce stricter rules in the subclass
Using the example; we break the programming principle by stating, as a property that
class Instructor, must not only be an instructor but also be an instructor of a specific institution. Pause a little and reason this out.
We are modifying the parent class from within the child. This is similar to saying a dog species class, say
Grayhound, modifies a parent class,
Dog, to have the color red. We just break everything else that depends on that single class
Dog. We have, essentially, stated that all dogs are red, even though it is just this one species.
Interface Segregation Principle
Make fine-grained interfaces that are client-specific.
We have a program, a class,
Library, that helps manage our books. Creating an instance of this class is the same as interfacing with it.
Take, as another example, an e-commerce platform. We lean towards microservices at this point. We have a database that houses our stall, an admin panel for the store owner and the user navigation section, where our buyers get to see and hopefully purchase our products. As microservices, at the top level of this becomes:
- Admin panel microservice
- Client microservice(we assume our buyers are the users)
Why should the buyer interface with the admin panel when they are not using it in the first place? Why would all that code be with them at that specific instance?
Dependency Inversion Principle
Depend upon abstractions, [not] concretions
The last of the solid principles, which, if you have been following those before, should fall right into place. A higher class should always depend upon the abstraction of the class rather than the detail. A good example of this is the implementation of abstract classes in Django models; the Abstractuser model.
In our application models, we would, upon migration, have models from classes that inherited from it, but have no table within the database, to represent the abstract model itself, because it's, well, abstract. You may note, how:
- High-level modules do not depend on low-level modules. Both should depend on the abstraction.
- Abstractions should not depend on details. Details should depend on abstractions.
A high-level module in any program is one that depends on others. We specify, I repeat, abstraction; an interface upon which we build.
DRY(Do not repeat yourself)
Oh, duplicate code, where have you been? You have a piece of code from A that is exactly familiar to the one in C. you have a
div, (a little web development for a while), across multiple pages. Repeating the same CSS styles and methods across those multiple pages. Why not make it a component in itself? Then tag it and write one CSS file and so forth and change data based on props?
KISS(Keep It Stupid Simple)
Often than not, engineers find themselves getting lost in algorithms and data flow other than the value the application is going to bring to the table. We focus more on 'features' as opposed to what the user will actually want our program to do.
So go forth engineer, KISS it, keep it SOLID, keep it DRY and remember to install libraries you actually need. No bloatware! Even on your current device. Do you need that app or is it there for that one time you thought about it?