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.

Functional Programming Vs. Object Oriented Programming

Object oriented programming has been the de-facto programming methodology since they day I learnt that there is something called computer programming. Several of the most popular programming languages are primarily object oriented programming languages. The most commonly asked interview questions for programmers are about object oriented programming.

Until, functional programming just blew up a few years ago. Functional programming languages have been there since the 1960s mind you, but only a few years ago they gained traction among ‘commercial’ developers. I found a ton of people learning Scala and lambdas and observables and all the associated jargon. A group of us jumping ships and embracing functional programming as the way to go for all our new projects. And another group of us sticking to the familiar grounds that is object oriented programming.

So far I’ve done two projects in the functional programming style, both web applications. One in Scala and one in Java (Spring Reactor). Here’s what I’ve learnt so far as contrasts between Object Oriented Programming and Functional Programming.

Core Principle

The first difference that we need to appreciate is the core principle guiding these methodologies. Whenever I see coders struggling to adopt functional programming, it is because they don’t have a grasp of this.

Object Oriented

  • In object oriented programming, we think of everything as objects with state. The flow of the application is dictated by change in state of the objects involved.

Functional

  • In functional programming, we think of everything as operations. The flow of the application is dictated by the chain of operations.

Design Patterns

If you are like most other programmers, you would have learnt a bunch of design patterns to apply in your projects. And I bet many of them are not just ‘design patterns’, they are ‘object oriented programming design patterns’. You need to even unlearn many of these. If you try to apply these common design patterns when you do functional programming, it will be like hammering a square peg in a round hole.

For the last couple decades or so, object oriented programming has overshadowed all the other ways of programming and this has one bad side effect – so much of the learning material depends on real world analogies. We’ve all come across things like the Animal --> Dog --> Dalmatian kind of analogies right? Well you need to forget all that and realise that abstractions are only there to lighten the cognitive load.

Computer doesn’t do dogs and cats. Computers do sets of operations on very basic units of data. Data like bits and bytes. Functional programming relates more to this attitude in a programmer. You are not working with simulated models of objects. You are taking an input, doing some processing and giving an output. Try to apply that to any programming task that you do. For example, a web service, takes a request as an input, does its processing stuff, and then sends a response as an output. This can be broken down into several levels to achieve cognitive ease, by breaking down the processing into multiple functions, and then chaining them by giving the output of one function as the input to the next function.

Choosing between the Two

It is not simple to classify an application, because most applications fall into multiple categories for purposes like these. Think about the core purpose of your application and make your decision based on that.

Object Oriented

  • Object oriented programming is preferable when you are representing a world of objects. For example a simulator, or a video game.

Functional

  • Functional programming works well in scenarios where your application is a processing pipeline. For example a event stream processor or a data processing API.

When you are trying to choose between the two, do not think of peripheral activities like logging, IO (even database updates). Rather, think of the main purpose your application is solving for its user. Is it giving your user an object-state model that they can manipulate? Or is it providing an engine that transforms their input and gives them an output?

Combining the Two

In real world, you probably are going to combine the two programming paradigms, rather than use strictly only one. Functional programming seems like a good fit for a web service, but some of the components are still better represented by an object oriented model. We can think of the web service as a series of functions that, in the beginning take in a request, and in the end emit a response. But entities like service and repository can still stay as objects. In fact, a pure function will not have side effects, but this is hardly useful in the real world right? We need to almost always have side effects – update a database, write to a log file, send out an email and so on.

The Code

The most visible differences between the two programming methods is when you look at the code. Some important differences are –

Object Oriented

  • Objects are the core entities. The program flow is usually instantiating objects and modifying their states. Any processing that is done, is as a means to change some state.
  • Control flow in object oriented programs is done through simple and traditional constructs like loops and if-else blocks.
  • There is a global scope, then there is a session scope, then there is a thread scope. Or simply put, there is always some global state from where you can get your environment variables from. The ‘context’ (that holds things like current user, configuration parameters etc) are available from their respective scopes.
  • Concepts like threads and concurrency are handled by the application code. Even if you use a multi-threading friendly framework, you still might have to declare what is thread-safe and so on.

Functional

Functions are things. You create functions and assemble them in chains. You can assign functions to variables and pass them as arguments to other functions.

  • Control flow in functional programming is done by chaining, filtering and recursion. Streams are preferred to collections.
  • There is only a local scope. In functional programming, the best practice is to provide everything your function needs as arguments. The ‘context’ (that holds things like current user, configuration parameters etc) gets passed in as an argument to all functions that need it.
  • Since a ‘global state’ is not even assumed, programs are by default thread safe. And most functional programming platforms manage concurrency by themselves, upon this same assumption.

Conclusion

To put in a simple way, when you are doing functional programming, don’t think of objects and types – rather, think that you are making a lot of small black boxes. Each one takes an input and gives an output. And then you’re arranging all those black boxes to make a useful application. If you can grasp this, you’re probably going to have a very easy time settling into functional programming.

Are Unit Tests Over-Rated?

Having worked in software development for more than 12 years now, I can safely say that about 20% of the projects I worked on, had unit tests. And none, had unit tests that were actually effective. Yet, I wouldn’t call even one of those applications as poorly written or of low quality. In fact, most of them were quite good – even in terms of maintainability and code quality.

One reason why I think unit test presence was low, is that all software I worked on were customer facing web applications. The emphasis was more on integration tests. I can’t help but think – is it really bad? I think it’s not. While I do recognise a lot of situations in which unit tests might help save the day, I don’t believe in having mandatory requirements of meeting a certain percentage in code coverage. The project I’m working on currently for a multinational corporation, has a 90% code coverage requirement.

Are your unit tests actually testing your code? Or are they just helping you achieve a code coverage metric?

Reasons against Writing Unit Tests

1. Code is not Reused Anyway

In a web application, except for a very little part, methods are always single use. When the code is divided into methods, its mostly for reducing complexity, making it readable and in general having modularity. Think of your typical method – validateTableCreationPayload(), createUser(User user), deleteBook(int bookId) and so on. These methods are going to be used in only one other place in your application right?

It’s quite easy to ensure changing the method doesn’t break the one, single usage of it. And when it does break, almost always, the unit test is what gets rewritten. That’s the thing with application code – you’re not going to deny your client’s requirement just because your unit test fails right? And there is nothing dependent to be changed – remember there’s only one usage. Considering that none of the functionality is breaking – and your integration tests ensure that, unit tests lose much of their worth anyway. And it becomes a ritual to just change the unit test whenever we change code.

2. Sacrificing Development Speed

Unit tests for application code will take as much time developing the actual code. Often, the unit tests take more time to develop than the actual functionality. An architectural purist might not even consider this as an issue. But a business manager will be – Are you saying it actually only needs half the time to develop!?

And unit tests tend to become very complex very fast. In a regular web application, you end up having to mock several components in almost every unit test. As far as I’ve seen, going beyond a certain coverage percentage, makes unit tests unreadable and off-putting to modify.

3. Misleading Sense of Security

You should always do proper diligence before modifying existing functionality. Do not ever rely on unit tests to catch these. Because unit tests are almost always insufficient. Take this example –

public void createUser(User user) {
    addMetaDataToUser(user);
    // new statement inserted here
    repository.createUser(user);
}

@Test
public void createUserShouldCallRepository() {
    createUser(dummyUser);
    verify(mockRepository).createUser(any());   
}

The above test has 100% coverage for the createUser method. But what happens if we insert another statement in the createUser method? The test still would show 100% coverage. The developer who inserts that statement can get away with not even writing unit tests for their change. Such things can only be caught by careful manual review. And unit tests are often ignored when code reviews are done. Especially in teams where an automated process reports code coverage metrics, and (wrongly) says everything is fine.

Worse, the above unit test is inadequate. It only checks if the repository.createUser() method is called. Not that it’s called with the correct argument! If the new inserted statement modifies the user argument, the unit test will still pass, code coverage will still be 100%. Bad, no?

Reasons for Writing Unit Tests

1. Documents Code

If unit tests are well written, they will act as documentation for how and why code is written in a certain way. It’s not uncommon to see a comment in code that says ‘ugly hack .. do not change this’ or something similar to that. The programmer who wrote it understands that someone in future might modify it and the fallout might not be immediate. But this assumes that nobody would have ‘cleaned up’ that comment. Or a future programmer would simply miss reading it. But if there is a unit test that looks out for this situation, that’s a way better safeguard.

Also, well written unit tests have great names that set expectations about the method being tested – shouldThrowExceptionForBadInput(), shouldReturnAValidUuid() etc. Also, these are arranged in neat groups in your test classes. So if your developers follow discipline you can take a look at the unit tests and learn a lot about the code.

2. Protects Sensitive Methods

General purpose methods, which are used all over the application need to be protected from change. For example, if you have a validation method that checks if a string has a particular pattern (maybe your company has a certain pattern for unique IDs), it is very risky if someone modifies this method. Every method you have used twice or more in your application, must have a unit test. The method serves for two tasks. What if you change it for one task and the method no longer works correctly for the second task? Preventing this, is the core purpose of unit tests. Unit tests serve as a guarantee to what the method will do and will not do.

3. Improves Code Quality

In my opinion, this is the number one reason to enforce unit tests. Especially if there are inexperienced developers in your team. If you write bad code, it becomes more difficult to write unit tests. In my initial days as a developer every time I struggled to write a unit tests, it showed me how I could have written my code to be more loosely-coupled, modular and simple.

Your function might have to be broken into pieces, if you are writing too many unit tests for it. Your function is too tightly coupled, if you are writing too many mocks. Are you finding you need to inject ‘private’ dependencies using Reflection? Are you unable to write a unit test because one of your private fields is initialised indirectly (like Spring’s @Value annotation)? Just about every time you find yourself writing unit tests that are too complicated, you can see opportunities to simplify your original code.

Conclusion

Now, if I propose we write only minimum required unit tests, I would be no different from the people who tell me to write the maximum possible unit tests. But one thing is obvious to me, having a code coverage target is pointless. Only the developer who is writing code can decide which part of it has to be registered in unit tests and which parts don’t quite benefit from unit testing. If you don’t give importance to unit tests in code reviews, don’t even bother requiring them – at least your development will be faster and your code base will be cleaner.