SOLID in example swift

Single Responcibility


class Animal {
    private let name: String
    init(_ name: String) {
        self.name = name
    }
    func getAnimalName() {}
    func saveAnimal(_ name: String) {}
}
It makes two action, getting the animal and saving to somewhere
class Animal {
    private let name: String

    init(_ name: String) {
        self.name = name
    }
    func getAnimalName() {}
    func saveAnimal(_ name: String) {}
}
We need to brake the code in two parts. One is working with animal and the other is saving to somewhere

Open Close Principle

   
class Animal {
    private let name: String
    init(name: String) {
        self.name = name
    }
    func getAnimal() -> String {
        return name
    }
}

let animals: [Animal] = [
    Animal(name: "lion"),
    Animal(name: "mouse")]

func animalSound(animals: [Animal]) {
    for animal in animals {
        if animal.getAnimal() == "lion" {
            print("roar")
        }
        if animal.getAnimal() == "mouse" {
            print("squeak")
        }
    }
}

If you need to add an animal you will have to add the lines to the code every time

class  Animal {
    func makeSound() -> String {
        return "Any animal"
    }
}
class Lion: Animal {
    override func makeSound() -> String{
        return "roar"
    }
}
class Mouse: Animal {
    override func makeSound() -> String {
        return "squeak"
    }
}

let animals: [Animal] = [
    Lion(),
    Mouse()]

func animalSound(animals: [Animal]) {
    for animal in animals {
        print(animal.makeSound())
    }
}

Liskov Substitution

if there is somewhere in the code checking the type the principle is broken. Subclass should be changeable for its superclass

class Animal {
}

class Lion: Animal {
    func legCount() {
        //
    }
}
class Mouse: Animal {
    func legCount() {
        //
    }
}
let animals: [Animal] = [Lion(), Mouse()]

func getLegCount(animals: [Animal]) {
    for animal in animals {
        if type(of: animal) == Lion.self {
            print("lion 4")
        }
        if type(of: animal) == Mouse.self {
            print("mouse 4")
        }
    }
}

The method getLegCount is not supposed to know the type.

class Animal {
    func legCount() -> String {
        return "Any Count"
    }
}

class Lion: Animal {
    override func legCount() -> String {
        return "Lion 4"
    }
}
class Mouse: Animal {
    override func legCount() -> String {
        return "Mouse 4"
    }
}
let animals: [Animal] = [Lion(), Mouse()]

func getLegCount(animals: [Animal]) {
    for animal in animals {
        print(animal.legCount())
    }
}

Interface Segregation

Clients should not be forced to depend on methods that they do not use. When a Class is required to perform actions that are not useful, it is wasteful and may produce unexpected bugs if the Class does not have the ability to perform those actions. A Class should perform only actions that are needed to fulfil its role. Any other action should be removed completely or moved somewhere else if it might be used by another Class in the future.

protocol Shape {
    func drawCircle()
    func drawSquare()
}
class Circle: Shape {
    func drawCircle() {
        //
    }
    func drawSquare() {
        //
    }
}
class Square: Shape {
    func drawCircle() {
        //
    }
    func drawSquare() {
        //
    }
}

It should be like single responsibilities but for interfaces

protocol ICircle {
    func drawCircle()
}
protocol ISquare {
    func drawSquare()
}
class Circle: ICircle {
    func drawCircle() {
        //
    }
}
class Square: ISquare {
    func drawSquare() {
        //
    }
}

Dependency Inversion

High-level modules should not depend on low-level modules. Both should depend on the abstraction. Abstractions should not depend on details. Details should depend on abstractions.

class Network: URLSession {}

class Http {
    private let session: Network
    init(session: Network) {
        self.session = session
    }
    func read(from url: URLRequest) {
        session.dataTask(with: url) { _, _, _ in
            //
        }.resume()
    }
}

Should be something like this

protocol Connection {
    func request()
}
class NetworkConnection: Connection {
    private let session: URLSession
    init(session: URLSession) {
        self.session = session
    }
    func request() {
        guard let url = URL(string: "path") else { return }
        let urlRequest = URLRequest(url: url)
        session.dataTask(with: urlRequest) { _, _, _ in
            //
        }
    }
}
class DBConnection: Connection {
    func request() {
        //
    }
}
class DataManager {
    private let session: Connection
    init(session: Connection) {
        self.session = session
    }
}