Cactus Tech Blog
Published in

Cactus Tech Blog

An Introduction to the S.O.L.I.D 5 Principles of Object-Oriented Design

Your SOLID guide to write better, cleaner, and more flexible code

In order to maintain high-quality code, it is crucial to create code that is quicker to develop on, more amenable to changes, and less susceptible to bugs. For this purpose, in the year 2000, Robert C. Martin (famously known as Uncle Bob) devised a set of software design principles combined to cleverly form the mnemonic acronym SOLID.

SOLID principles of Object Oriented Design

Single Responsibility principle

This principle states that a class should have a single responsibility i.e. it must solve a single problem. Only changes to a certain area of the software’s specification will be able to alter the class. This ensures that a class only has one reason to change.

Take, for example, the (simple) class called Page.

class Page {
protected $page
public page($title) {
return $this->page;
}
public function formatIntoJson() {
return json_encode($this->page());
}
}

This class knows about a title property and allows this title property to be retrieved by a get() method. We can also use a method in this class called formatIntoJson() to return the page as a JSON string. This is completely hassle-free as the class is responsible for its own formatting.

What happens, however, if we want to change the output of the JSON string, or add another type of output to the class? We would need to alter the class to either add another method or change an existing method. This is fine for a class as simple as this, but if it contained more properties, then the formatting could be slightly more complicated.

class Page {
protected $page;
public getPage($page) {
return $this->page;
}
}
class JsonPageFormatter {
public function format(Page $page) {
return json_encode($page->getPage());
}
}

Open/Closed Principle

According to the open/closed principle, classes should be open for extension but closed for modification. Simply put, if something has been tested and works for you, don’t change it. You may run the risk of introducing new bugs to your program. Instead, you can extend that class in order to add new features, rather than altering it

As an example, take the following two classes:

interface Shape {    
public function area();
}
class Square implements Shape {
public function area() {
return side*side;
}
}
class Rectangle implements Shape {
public function area() {
return $this->width * $this->height;
}
}

We created Rectangle and Circle shape classes that will calculate their area when asked, instead of creating a single class which calculates the area using lots of ‘if’ and ‘else’ statements

Liskov Substitution Principle

The Liskov principle is named after Barbara Liskov when she introduced the concept in her keynote on ‘Data Abstraction’ in 1987. The Liskov Substitution Principle states that any class that is the child of a parent class should be usable in place of its parent without any unexpected behavior.

Let’s understand this using an example:

interface Lesson {    
public function getAll();
}
class File implement Lesson {
public function getAll() {
return $files;
}
}
class Database implements Lesson {
public function getAll(){
return Lesson::all()->toArray();
}
}

As you can see from the code above, both classes implement the lesson using the getAll method.

Interface Segregation Principle

This principle states that many client-specific interfaces are better than one general-purpose interface. In other words, classes should not be forced to implement interfaces they do not use. An extension of the single-responsibility principle, the goal here is to split the software into multiple highly-specific parts rather than having a general-purpose interface.

To illustrate this, let’s look at a Worker interface. This defines several different methods that can be applied to a worker in a typical development agency. For now, I am only taking one method into consideration, which is quite possibly, an employee’s favorite thing to do.

interface Worker {
public function takeBreak();
}
class Manager implements Worker {
public function code() {
return true;
}
}
class Developer implements Worker {
public function code() {
return false;
}
}

Dependency Inversion Principle

This principle states that classes should depend upon abstractions, not concretions. What this essentially means is that higher-level classes, with more complex logic and functions should remain unaffected by lower-level classes, depending instead, on interfaces.

For instance,

interface ArticleInterface
{
public function getAll();
}
class CRMArticleRepository implements ArticleInterface
{
public function getAll()
{
return CRMArticle::all();
}
}
class EOSArticleRepository implements ArticleInterface
{
public function getAll()
{
return EOSArticle::all();
}
}

In the above example, the two different classes don’t depend upon concretions, but they depend on the interface.

A deep understanding and application of the SOLID principles will not only help you deliver code that can be easily modified, tested, and refactored, but it will also help you improve your programming skills considerably.