top of page
  • Writer's pictureVaughn Geber

SOLID Programming: Thanks Uncle Bob

In software development, there are many principles and best practices that programmers should follow to write high-quality, maintainable, and scalable code. One of the most popular and widely used sets of principles in object-oriented programming is the SOLID principles. These principles were introduced by Robert C. Martin (also known as Uncle Bob) and are aimed at helping programmers design software systems that are easy to maintain, extend, and refactor.





In this blog post, we will discuss the five SOLID principles and provide examples of how these principles can be implemented in Swift programming language.


1. Single Responsibility Principle (SRP): The Single Responsibility Principle (SRP) states that a class should have only one responsibility, meaning that it should have only one reason to change. In other words, a class should do one thing and do it well. This principle helps to keep code organized, easy to maintain, and less prone to errors.


Example: Let's say we have a class named "Order" that is responsible for both processing orders and sending confirmation emails to customers. This violates the SRP because the class has two responsibilities. We can separate the email sending functionality into a separate class like "EmailSender" to follow SRP.

class Order {
    func processOrder() {
        // process order
    }
    
    func sendConfirmationEmail() {
        let emailSender = EmailSender()
        emailSender.sendEmail()
    }
}

class EmailSender {
    func sendEmail() {
        // send email
    }
}

2. Open-Closed Principle (OCP): The Open-Closed Principle (OCP) states that a class should be open for extension but closed for modification. This means that we should be able to extend the functionality of a class without modifying its source code. This principle encourages the use of abstraction and interfaces to decouple components and make them more flexible and reusable.


Example: Let's say we have a class named "Shape" with a method named "calculateArea". Instead of modifying the Shape class every time we want to add a new shape, we can create a protocol named "ShapeProtocol" and make Shape conform to this protocol. Then, we can create new classes that conform to the ShapeProtocol and add their own implementation of calculateArea without modifying the Shape class.

protocol ShapeProtocol {
    func calculateArea() -> Double
}

class Shape: ShapeProtocol {
    func calculateArea() -> Double {
        return 0.0
    }
}

class Circle: ShapeProtocol {
    func calculateArea() -> Double {
        // calculate area of circle
    }
}

class Square: ShapeProtocol {
    func calculateArea() -> Double {
        // calculate area of square
    }
}

3. Liskov Substitution Principle (LSP): The Liskov Substitution Principle (LSP) states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program. This means that a subclass should be able to override methods of the superclass without changing their behavior or violating their contracts. This principle ensures that the code is more robust and maintainable.


Example: Let's say we have a class named "Vehicle" with a method named "startEngine". We can create a subclass named "Car" that overrides the startEngine method to include additional functionality without violating the LSP.

class Vehicle {
    func startEngine() {
        // start engine
    }
}

class Car: Vehicle {
    override func startEngine() {
        // do additional things
        super.startEngine()
    }
}

4. Interface Segregation Principle (ISP): The Interface Segregation Principle (ISP) states that a client should not be forced to implement methods that it does not use.


This means that we should avoid creating large interfaces that contain many methods, and instead, we should create smaller, more specific interfaces that are tailored to the needs of each client. This principle helps to reduce the coupling between components and makes the code more maintainable and flexible.


Example: Let's say we have a protocol named "Printer" that has two methods "printDocument" and "scanDocument". If we have a client that only needs to print documents, we can create a smaller interface named "Printable" that only contains the "printDocument" method, and have the client implement this interface instead of the larger "Printer" interface.

protocol Printer {
    func printDocument()
    func scanDocument()
}

protocol Printable {
    func printDocument()
}

class Client: Printable {
    func printDocument() {
        // print document
    }
}

5. Dependency Inversion Principle (DIP): The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules, but both should depend on abstractions. This means that we should decouple components by relying on abstractions (interfaces) instead of concrete implementations. This principle helps to reduce the coupling between components, make the code more modular, and easier to test.


Example: Let's say we have a class named "OrderProcessor" that depends on a low-level class named "Database". We can decouple the OrderProcessor class by introducing an interface named "DatabaseProtocol" and making the Database class conform to this protocol. Then, we can pass an instance of the Database class to the OrderProcessor class via dependency injection.

protocol DatabaseProtocol {
    func saveOrder()
}

class Database: DatabaseProtocol {
    func saveOrder() {
        // save order to database
    }
}

class OrderProcessor {
    var database: DatabaseProtocol
    
    init(database: DatabaseProtocol) {
        self.database = database
    }
    
    func processOrder() {
        // do some processing
        database.saveOrder()
    }
}

Conclusion:

The SOLID principles provide a set of guidelines for designing object-oriented software systems that are easy to maintain, extend, and refactor. By following these principles, programmers can create more robust, flexible, and scalable code. In this blog post, we provided examples of how each of the five SOLID principles can be implemented in Swift programming language. By applying these principles, developers can write code that is more maintainable, testable, and adaptable to changing requirements.



17 views0 comments

Recent Posts

See All

Comentarios


bottom of page