How to Write Better Object Oriented Code — SOLID Principles

Using “Uncle Bob’s” five object oriented design principles to write better code.

Caelin Sutch
4 min readNov 18, 2020
Photo by Clément H on Unsplash

SOLID is an acronym for the first five object oriented design (OOD) principles, created by Robert C. Martin, popularly known as “Uncle Bob”. Developed in Martin’s book “Agile Software Development, Principles, Patterns, and Practices”, the SOLID principles have become industry standard practices to write and release better code.

These principles, when combined and used properly, make it easy for programmers to write software that’s easy to maintain and extend. They are also part of the agile or adaptive software development style, and ensure code remains clean and easily refactorable.

SOLID stands for:

  • S - Single Responsibility
  • O - Open Closed Principle
  • L - Liskov Substitution Principle
  • I - Integration Segregation Principle
  • D - Dependency Inversion Principle

Lets take a look at each of these principles to understand how SOLID makes us better developers.

Single Responsibility Principle

Also coined SRP, this principle states “A class should have one and only reason to change, meaning that a class should have only one job”.

SOLID suggests that each responsibility of a class should be defined as a reason for change. If you can think of more than one motivation for changing a class, it probably has more than one responsibility and should be broken up.

The key here is creating a bunch of smaller classes that are built to handle a specific function, rather than a God class than handles everything.

As an example, lets look at this Book class:

This looks reasonable right? We have a book with a title, author, and it can turn a page. Finally, it can print the page via a console log. But there’s a problem here. Lets think about the responsibilities of this class. The class can manage the book (like the author and current page), but it can also present the data. Ah, here we see a problem, this class is handling two very different responsibilities!

Mixing business logic with presentation logic is bad because it violates SRP. Lets refactor this so it’s a bit cleaner:

Much better, now we have separated out responsibilities into two separate classes. This is one example of how it can be easy for us to mix responsibilities of classes.

Open Closed Principle

Martin defines this as “Object or entities should be open for extension, but closed for modification”.

This basically means that a class should be easily extendable without having to modify the class itself. Lets say we wanted to print a couple different types of HTML besides the “single-page” style we are using depending on the book being printed. Your first thought might be to implement some sort of case statement to customize the printer, but this violates the first rule!

A way we can make this printPage function better is by attaching the specific HTML style element to the shapes class and implement an interface to ensure we will always have access to this pageStyle property:

See how it’s really easy to extend the Book class to add additional functionality?

Liskov Substitution Principle

The definition for this one is a little scary: “Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.”

Really, all this is stating is that every derived class should be a substitute for the base/parent class. For example, if we were to create a new PictureBook class that extends Book , we should be able to swap out any places where Book is with a Picture Book and it should always work fine.

Interface Segregation Principle

A client should never be forced to implement an interface that it doesn’t use or clients shouldn’t be forced to depend on methods they do not use.

The point of this principle is to ensure classes and interfaces are kept small and refrain from getting bloated. If we wanted to represent comic books in our code, we would create a separate interface that has a object variable for the different panels on the comic book page rather than implementing that object variable on the base class.

With this principle, you will ensure that each interface is very specific in functionality and easy to maintain.

Dependency Inversion Principle

The last, but not least, states “Entities must depend on abstractions not on concretions. The high level module must not depend on the low level module, but should depend on abstractions”.

This may sound confusing, but it’s pretty easy to understand. This principle allows for code decoupling. Lets look at an example that will explain this principle:

The DBConnection is what’s considered a low level module while the PasswordReminder is high level. This class violates the D principle since the PasswordReminder class is forced to depend on the MySQLConnectionclass.

If you wanted to change the database later on, you’d have to update PasswordReminder . SOLID says that PasswordReminder should not care what database your application uses. To fix this, lets code to an interface:

interface DBConnectionInterface {
public function connect();
}

Now we can abstract out the dependency on MySQLConnection and have PasswordReminder depend on DBConnectionInterface instead:

Now we can see that both the high level and low level modules depend on abstraction! This makes it easier to maintain and refactor both classes.

Conclusion

SOLID may be a little abstract, but hopefully some of these examples made it clear how you can adhere to and use SOLID to write better code. Code that follows SOLID is more testable, extendable, refactorable, and easier to work with.

--

--

Caelin Sutch

Founder, engineer, designer. Passionate about building cool shit.