SOLID principles

One of the biggest advantages of object oriented programming is the amount of good practices and design patterns that have been researched and documented. Most of these exist so that introducing changes in future is easy and safe. Once a piece of software reaches critical mass, it inevitably ‘hardens’ – making changes to it is risky, it becomes extremely tough to even read and understand. Someone who attempts to fix a bug or extend a functionality, feels threatened that they might break something by making their change – and rightly so. If code is not well written, something will almost always break when a change is made.

Being able to write code which is easy to understand, maintain and extend is what separates the great coders from the thousands of people who simply write code that barely works. The SOLID principles is one set of simple guidelines that are extremely effective in elevating the quality of your code. If you don’t already know, SOLID is an acronym for 5 principles, namely –

  1. Single Responsibility Principle: Each class has one and only a single responsibility.
  2. Open-Closed Principle: Classes should be open for extensions, but closed for modifications.
  3. Liskov Substitiution Principle: Subclasses should be substitutable for parent classes.
  4. Interface Segregation Principle: Interfaces should be segregated into multiple, small, specific ones.
  5. Dependency Inversion Principle: A class should not be used directly inside another class. It should be abstracted using an interface.

I’ll explain my understanding of the 5 concepts in a bit more detail here.

Single Responsibility Principle

This one is easy to get. A class you create, should have only one responsibility. For example, when you are developing a web application, you will probably be using the MVC pattern. Most web applications also involve authorizations – like whether the current user have permissions to edit this record. Where does the code for doing this authorization go? If it is in the controller class, then the single responsibility principle is broken. The controller code should have only code that does the orchestration. Another example is validation. Your application should have a class that has all your validation code. The controller should only use this validation class to perform validations.

In the following code, UserController has only one responsibility – to orchestrate all the steps. Validator class has only one responsibility – to validate requests. Authorizer class has only one responsibility – to perform authorization.

class UserController {
    Validator userValidator;
    Authorizer userAuthorizer;

    public UserController(Validator userValidator, Authorizer userAuthorizer) {
        this.userValidator = userValidator;
        this.userAuthorizaer = userAuthorizer;
    }

    public Response createUser(Request request) {
        if(!userValidator.isValidRequest(request)) {
            return ErrorResponse("400: Bad Request");
        }
        if(!userAuthorizer.isAuthorized(request)) {
            return ErrorResponse("403: Forbidden");
        }
        // .. code to create user .. //
    }
}

Open Closed Principle

Classes should be open to extension but closed for modification. Let me put this principle in two parts –

  • It should be possible to extend a class when requirements of the application change. If you are building a bookstore application, you might have an Order object inside a OrderProcessor class. Your application should allow for future extension like ‘urgent’ orders.
class BookStore {
    OrderProcessor op;
    op.process()
}
class OrderProcessor {
    Order o;
    public void process() {
        // order processing logic
    }
}
  • It should not be possible to modify a class. This means, if you introduce a new class like ‘UrgentOrder’, you should not modify code inside the OrderProcessor class. Like ‘if (order instanceof UrgentOrder) … ‘ etc.
/* This is not allowed ! Closed for modification => must not change the OrderProcessor class. */
class BookStore {
    OrderProcessor op;
    op.process()
}
class OrderProcessor {
    Order o;
    public void process() {
        if (o.type = normal) {
            // order processing logic
        } else if (o.type == urgent) {
           // urgent order processing logic
        }
    }
}

The way we achieve this principle is by ensuring our classes will never be changed. We will only write new code to support new functionality. So this example, if written following the Open/Closed principle will be –

class BookStore {
    OrderProcessor op;
    public void execute() {
        op.process()
    }
    /* .. */
}
interface OrderProcessor {
    public void process();
}
class NormalOrder {
    Order o;
    public void process() {
        // order processing logic
    }
}
/* When a new type of order - 'urgent' order is introduced, we don't have to change the above code. Just write a new class implementing the OrderProcessor interface. */
class UrgentOrder {
    Order o;
    public void process() {
        // urgent order processing logic
    }
}

Liskov Substitution Principle

When you inherit a class, the new inherited class should be substitutable for the original parent class. That is, if your code has a ‘DatabaseConnection’ class, and you are extending this and creating a ‘CachedDatabaseConnection’, then this new type of object should be usable wherever the DatabaseConnection object was used.

If you are using a strongly typed language (like Java), you probably don’t even have to worry about this principle. It is automatically ensured that the extending class will be usable wherever the extended (parent) class was used. But if you are not using a strongly typed language, it is your responsibility to ensure this principle is not broken. An example scenario (in JavaScript) that breaks this principle –

class Item {
    stockQuantity() {
        return 5;
    }
}

class SpecialItem extends Item {
    stockQuantity() {
        return {"warehouse1":3, "warehouse2":2}
    }
}

The above example breaks Liskov Substitution principle because other code expects Item.stockQuantity to return a simple integer. If the new SpecialItem is substituted there, Item.stockQuantity will return a compound object and the application might break!

Interface Segregation Principle

I like to think of interface segregation principle as same as the single-responsibility principle, but for interfaces. It simply means that your interfaces should have enough segregation between them so that no class will have to implement methods not applicable for them.

interface Animal {
    public void swim();
    public void fly();
}

class Shark implements Animal {
    public void swim() {
        // I'm swimming
    }
    public void fly() {
        // Error: I'm a shark! I can't fly!
    }
}

class Crow implements Animal {
    public void swim() {
        // Error! I'm a crow, I can't swim!
    }
    public void fly() {
        // I'm flying
    }
}

The above example breaks interface segregation principle, because the Shark class has to unnecessarily implement the fly method, only to throw an error. Similarly the Crow class implements the swim method. To make this code compliant with interface segregation, the Animal interface should be segregated into two interfaces.

interface Fish {
    public void swim();
}

interface Bird {
    public void fly();
}

class Shark implements Fish { ... }
class Crow implements Bird { ... }
class FlyingFish implements Fish, Bird { ... }

Now that the interfaces are segregated, we can implement sharks, crows without having to write unnecessary code. And when a new class needs both functionality, we just implement both interfaces.

Dependency Inversion Principle

Using a class inside another class couples them tightly. Dependency inversion avoids this by introducing a layer of abstraction.

For example, a repository class will have a database connection object as a member. Now the repository class depends on the database connection object. If the database connection object needs to be changed (for example, you decide to use MySQL database instead of Oracle), even though the functionality doesn’t change, the repository class needs to be rewritten to use the new Oracle database connection object.

In the above example, if dependency inversion is deployed, the repository will no longer be dependent on the MySQL connection object. It will be dependent on an ‘interface’ like DBConnection. Now both MySQL and Oracle connection objects should ‘implement’ this DBConnection interface. In other words, the repository object, the MySQL connection, and the Oracle connection have all been made to depend on the DBConnection interface. The dependency has been ‘inverted’. As long as the MySQL and Oracle connection classes implement the DBConnection interface, they can be switched seamlessly.

Conclusion

So there, I’ve tried to explain the SOLID principles in the shortest possible way that I could think of. SOLID principles are like the foundations of object oriented programming – and this article doesn’t do justice to their importance at all. Watch this video – https://www.youtube.com/watch?v=zHiWqnTWsn4 to get a deeper understanding of these principles from Mr. Robert Martin. Disclaimer! The name of that YouTube channel might be offensive to some – I’m not related to that channel in anyway, I don’t condone bad language, I do not have any informed opinions about subjects like communism.

New Year Resolutions

Photo by Chandan Chaurasia on Unsplash

I know most of us hardly stick to new year resolutions. But I still like making them. It makes me size up my life and think of what I have and what I miss. For this year, I want to –

1. Get Off the Computer

If you’re the typical developer, you probably have all the other parts of your life on the computer too. Your friends are all online. You hobby is probably playing video games. Reading books, watching videos, doing their budget, writing down to-do lists. Almost everything you can think of, you’re probably doing on the computer. When you think of a side project – it’s probably something to do on a computer – writing a blog 🙄 , freelancing as a coder and so on. If you study you’re probably going to take one of the online courses and study on the computer.

Well this year, I want to get off the computer. Of course I’ve already started an online course and I’m writing a blog. But I don’t think those are taking up much of my computer time. Apart from my work, and a couple hours for those things, I think I don’t have to sit at my computer at all. A major chunk of computer time goes for gaming, Facebook, YouTube and Netflix. I can altogether drop those. I resolve to have my entertainment and social life, away from the computer. This means I’ll have to let go of some of the new and the cool. Even for studying and blogging, I’m thinking to partially move them away from the computer. Maybe I can write drafts on pen and paper before I sit to publish it on the computer. I can also study the online course using a tablet, take notes with a pen and paper.

Less computer. More real world. More pen-and-paper.

2. Learn Continuously

This is partially already done because of the nature of development jobs. You just can’t avoid learning. New frameworks and tools pop up every day. Parts of the job are getting obsolete and new parts are getting created. Lines between business and IT are getting blurred. If you’re a developer, the only way you can be successful, or even just survive for long, is if you keep learning new things. This much goes without saying.

But what about the other things? Do you know anything at all besides coding? Have you learnt music or an art? To play a sport? Do you know about economics? Do you know to cook? Pick one or two skills outside of coding, and work on it regularly. Maybe cook food on weekends. Learn a new language (a spoken language – not another programming language). Join a workshop on reparing cars. Practice it by yourself or join a class. Do something that expands your mind. Do something that makes you interesting.

Develop a new skill. Keep improving.

3. Take Health Seriously

Whenever I get pain in my shoulders, neck or lower back, I go through a ‘fitness phase’. I get the pain treated and follow a workout routine for a few days after that. But as the pain is relieved, so is my motivation. Not only fitness, but even looks can become invisible when most of your life is online through a computer. I mean who cares if you’re fat if no one ever sees you right? After two years of living alone as a developer, I have literally become that fat guy who’s only contact with outside world is with the food delivery guy. Literally!

I resolve to take health seriously. Prevention is better than cure. I’m targetting a healthy fitness level, eating good food, moderate regular exercise and frequent breaks for ergonomics. My chiropractor said the best posture is no posture at all. I like the way he put it. You need to move frequently so that you’re not really getting ‘fixed into a posture’ in the first place. That’s the only way to avoid posture related problems.

Do about half an hour of exercise, almost everyday. Include some fruits and vegetables in diet. Do not keep sitting for more than half an hour at a time. Go to bed early – without your smartphone.

4. Give to Relationships

This one is particularly hard for me because I’m a complete introvert. I am truly fine just being single rather than have to put up with a family. I simply hate even just being around people. But I’m resolving to this anyway. Because I’m old enough to know that this is important. Being around people, having face-to-face interactions, depending on and being depended upon, are immensely more satisfying than a lonely-yet-comfortable life. I, like many other developers, have always avoided social interactions with people. I don’t do small talk, I don’t attend ceremonies, I don’t even go to casual meetings at work.

I try to avoid my family as much as possible. Even though I like to spend time with my little nephews, I’d rather not because it would mean I have to be around the grown ups too. I need to stop being such a loner. Wisdom tells me that friends are important, family is important.

Make more friends and invest some time and effort into the ones that exist. Be in touch with family.

5. Travel

There are so many YouTube channels that make you yearn to just drop your ‘normal’ life and become a nomad with a back pack. But that’s simply impractical and unnecessary. Not everyone can (or should) become a full time traveller. But I believe vacations are necessary. They create a disconnect that simply can’t be achieved otherwise. I’m at the end of a two week holiday but doesn’t even seem like I left work. Because I just spent it all at home. Still at my computer. Still thinking about work.

Traveling – without your laptop – actually traveling, acts like a reset for the mind. You don’t have to travel to fancy places or spend a lot of money. Just to a simple place a few hundred kilometers away. Maybe for beautiful views of nature, maybe for spirituality of an ancient temple. Maybe solo, maybe with friends or family. But make it a point to do it.

Travel to a far away place. Atleast 3 days together. Atleast once in 3 months. Create memories.