SOLID Principles for Programming and Software Design

devwalid

Walid Bouladam

Posted on December 15, 2022

SOLID Principles for Programming and Software Design

Hey y'all!

This article will go over the following:

  • What are the SOLID principles?
  • Why are they used in Object-Oriented programming?

I will also provide examples of how we can implement the SOLID principles when programming, using PHP, which is the language I am currently using as I learn about Object-Oriented programming.

What are the SOLID principles?

The SOLID principles are a set of five principles of object-oriented software design intended to make software designs more understandable, flexible, and maintainable. The principles are:

  • Single Responsibility Principle: A class should have one and only one reason to change, meaning it should only have one job or responsibility.

  • Open/Closed Principle: A class should be open for extension, but closed for modification. This means that new functionality should be added by extending the class, rather than modifying its existing code.

  • Liskov Substitution Principle: Derived classes should be substitutable for their base classes. This means that any instance of a base class should be able to be replaced with an instance of a derived class without breaking the application.

  • Interface Segregation Principle: Clients should not be forced to implement interfaces they don't use. This means that you should split large interfaces into smaller, more specific ones so that clients only have to implement the methods that they need.

  • Dependency Inversion Principle: High-level modules should not depend on low-level modules. Both should depend on abstractions. This means that you should use abstractions, such as interfaces, to decouple your code and make it more flexible.

Why are they used in Object-Oriented programming?

The SOLID principles are important in Object-Oriented programming because they help to create software designs that are more understandable, flexible, and maintainable.

The Single Responsibility Principle, for example, helps to ensure that a class has a well-defined purpose and is not trying to do too many things at once. This makes the code easier to understand and reduces the likelihood of introducing bugs when making changes.

The Open/Closed Principle, on the other hand, promotes code reuse and extensibility by allowing new functionality to be added through inheritance rather than modifying existing code. This makes the code more flexible and easier to maintain.

Overall, following the SOLID principles can help to create more robust and scalable software designs.

Examples

Single Responsibility Principle

Here is an example of the Single Responsibility Principle being implemented in PHP:

// A class that handles user authentication
class UserAuth
{
    public function login($username, $password)
    {
        // Check the user's credentials and log them in
    }

    public function logout()
    {
        // Log the user out of the system
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the UserAuth class has a single responsibility, which is to handle user authentication. It has two methods, login() and logout(), which allow users to log in and out of the system. This class does not try to do anything else, such as managing user profiles or sending emails, and is therefore following the Single Responsibility Principle. This makes the code easier to understand and maintain.

Open/Closed Principle

Here is an example of the Open/Closed Principle being implemented in PHP:

// A base class for a bank account
abstract class BankAccount
{
    protected $balance;

    public function __construct($balance)
    {
        $this->balance = $balance;
    }

    public function getBalance()
    {
        return $this->balance;
    }

    abstract public function deposit($amount);
    abstract public function withdraw($amount);
}

// A class that extends the BankAccount class to add support for interest
class SavingsAccount extends BankAccount
{
    private $interestRate;

    public function __construct($balance, $interestRate)
    {
        parent::__construct($balance);
        $this->interestRate = $interestRate;
    }

    public function deposit($amount)
    {
        // Calculate the interest earned and add it to the balance
    }

    public function withdraw($amount)
    {
        // Withdraw the given amount from the balance
    }
}

Enter fullscreen mode Exit fullscreen mode

In this example, the BankAccount class is defined as an abstract base class, which means that it cannot be instantiated on its own. It defines two abstract methods, deposit() and withdraw(), which must be implemented by any derived classes. This allows the BankAccount class to be extended to support different types of accounts, such as savings accounts, without modifying its existing code. For example, the SavingsAccount class extends BankAccount and adds support for interest. This follows the Open/Closed Principle by allowing new functionality to be added through inheritance, rather than modifying the base class. This makes the code more flexible and easier to maintain.

Liskov Substitution Principle

Here is an example of the Liskov Substitution Principle being implemented in PHP:

// A base class for a rectangle
class Rectangle
{
    protected $width;
    protected $height;

    public function __construct($width, $height)
    {
        $this->width = $width;
        $this->height = $height;
    }

    public function getArea()
    {
        return $this->width * $this->height;
    }
}

// A class that extends the Rectangle class to represent a square
class Square extends Rectangle
{
    public function __construct($side)
    {
        parent::__construct($side, $side);
    }
}

Enter fullscreen mode Exit fullscreen mode

In this example, the Rectangle class defines a base class for a rectangle with a width and a height. It also has a getArea() method that calculates the area of the rectangle. The Square class extends Rectangle and represents a square, which is a special type of rectangle with equal sides. However, instead of defining its own width and height properties, it sets the width and height of the Rectangle class to the same value, which is the side length of the square. This allows any instance of the Square class to be used in place of an instance of the Rectangle class without breaking the application. This follows the Liskov Substitution Principle by ensuring that derived classes are substitutable for their base classes. This makes the code more flexible and easier to maintain.

Interface Segregation Principle

Here is an example of the Interface Segregation Principle being implemented in PHP:

// An interface that defines methods for a database connection
interface DatabaseConnection
{
    public function connect();
    public function query($sql);
    public function disconnect();
}

// An interface that defines methods for a MySQL database connection
interface MySQLConnection extends DatabaseConnection
{
    public function getMySQLVersion();
}

// A class that implements the MySQLConnection interface
class MySQL implements MySQLConnection
{
    public function connect()
    {
        // Connect to a MySQL database
    }

    public function query($sql)
    {
        // Execute a query on the MySQL database
    }

    public function disconnect()
    {
        // Disconnect from the MySQL database
    }

    public function getMySQLVersion()
    {
        // Return the version of the MySQL server
    }
}

Enter fullscreen mode Exit fullscreen mode

In this example, the DatabaseConnection interface defines three methods that must be implemented by any class that implements the interface: connect(), query(), and disconnect(). These methods are common to any type of database connection. However, the MySQLConnection interface extends DatabaseConnection and adds a fourth method, getMySQLVersion(), which is specific to MySQL connections. This allows the MySQLConnection interface to be implemented by classes that need to connect to MySQL databases, without forcing them to implement the methods from the `DatabaseConnection interface that they don't need. This follows the Interface Segregation Principle by splitting a large interface into smaller, more specific ones. This makes the code more flexible and easier to maintain.

Dependency Inversion Principle

Here is an example of the Dependency Inversion Principle being implemented in PHP:

// An interface that defines methods for a logger
interface Logger
{
public function log($message);
}

// A class that implements the Logger interface
class FileLogger implements Logger
{
public function log($message)
{
// Log the given message to a file
}
}

// A class that uses the FileLogger class to log messages
class User
{
private $logger;

public function __construct(Logger $logger)
{
    $this->logger = $logger;
}

public function login()
{
    // Log a message when the user logs in
    $this->logger->log('User logged in.');
}

public function logout()
{
    // Log a message when the user logs out
    $this->logger->log('User logged out.');
}
Enter fullscreen mode Exit fullscreen mode

}

In this example, the User class depends on the Logger interface to log messages. It does not depend on a specific implementation of the Logger interface, such as the FileLogger class. This means that the User class is not tied to a specific logging mechanism and can be used with any class that implements the Logger interface. For example, you could create a DatabaseLogger class that implements the Logger interface and logs messages to a database, and use it with the User class without modifying its code. This follows the Dependency Inversion Principle by depending on abstractions, rather than concretions. This makes the code more flexible and easier to maintain.

Summary

The SOLID principles are a set of principles of object-oriented software design that are intended to make software designs more understandable, flexible, and maintainable. The principles focus on the separation of concerns, the use of abstraction, and the dependency of high-level modules on abstractions rather than concretions. By following these principles, developers can create software designs that are more robust, scalable, and easier to maintain.

💖 💪 🙅 🚩
devwalid
Walid Bouladam

Posted on December 15, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related