SOLID Principles

SOLID Principles

Khoshimov Shavkatbek

Understanding the SOLID Principles: Building Better Software Design


The SOLID Principles are a set of five object-oriented design principles that can help you write better code. They are:

  • Single Responsibility Principle
  • Open/Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

How can you apply the SOLID Principles to Swift? 

There are many ways to apply the SOLID Principles to Swift. Here are a few examples:

  • Single Responsibility Principle: You can apply the Single Responsibility Principle by making sure that each class in your code has only one responsibility. This will make your code easier to understand and maintain.
  • Open/Closed Principle: You can apply the Open/Closed Principle by making sure that your code is open for extension but closed for modification. This means that you should be able to add new features to your code without having to change the existing code.
  • Liskov Substitution Principle: You can apply the Liskov Substitution Principle by making sure that derived classes can be used in place of their base classes without any problems. This means that you should make sure that derived classes do not violate the contract of their base classes.
  • Interface Segregation Principle: You can apply the Interface Segregation Principle by making sure that your interfaces are not too large. This means that you should only expose the methods that are actually needed by clients.
  • Dependency Inversion Principle: You can apply the Dependency Inversion Principle by making sure that your code does not depend on concrete classes. Instead, your code should depend on abstractions. This will make your code more flexible and easier to test.

What are the benefits of using the SOLID Principles?

There are many benefits to using the SOLID Principles. Some of the benefits include:

  • Better code quality: The SOLID Principles can help you write better code that is easier to understand, maintain, and test.
  • Increased flexibility: The SOLID Principles can help you make your code more flexible and easier to change.
  • Reduced coupling: The SOLID Principles can help you reduce coupling between your classes, which can make your code more modular and easier to reuse.
  • Improved testability: The SOLID Principles can help you make your code more testable, which can help you catch bugs earlier and improve the quality of your code.


Here are some examples of how the SOLID Principles can be applied to Swift:


Single Responsibility Principle:
  • A class that represents a car should only be responsible for representing a car. It should not also be responsible for driving the car or parking the car.

For example, you could create a class called Car that has the following properties:

var make: String
var model: String
var year: Int


The Car class would also have the following methods:

func getMake() -> String
func getModel() -> String
func getYear() -> Int


The Car class would not have any methods for driving the car or parking the car.

These responsibilities would be delegated to other classes.


*-*

Open/Closed Principle:
class Shape {
  func draw() {
    print("Drawing a shape")
  }
}

class Circle: Shape {
  override func draw() {
    print("Drawing a circle")
  }
}

class Square: Shape {
  override func draw() {
    print("Drawing a square")
  }
}

let shapes: [Shape] = [Circle(), Square()]

for shape in shapes {
  shape.draw()
}

In this example, we have a Shape class and two derived classes: Circle and Square. The Shape class has a draw() method that draws a shape. The Circle and Square classes override the draw() method to draw a circle and a square, respectively.

This design follows the Open/Closed Principle because we can add new shapes to the system without modifying the Shape class. To add a new shape, we simply create a new derived class that overrides the draw() method to draw the new shape.

The Open/Closed Principle is important because it helps to make code more flexible and easier to change. By following the Open/Closed Principle, we can make sure that our code can be easily extended to support new features without having to modify the existing code.


*-*

Liskov Substitution Principle:
class Animal {
  func makeSound() {
    print("Generic animal sound")
  }
}

class Cat: Animal {
  override func makeSound() {
    print("Meaw!")
  }
}

let animal: Animal = Cat()
animal.makeSound() // Prints "Meaw!"


In this example, the Cat class inherits from the Animal class. The Cat class overrides the makeSound() method to make a sound. However, the makeSound() method in the Animal class still works when it is called on a Cat object. This is because the Cat class does not violate the contract of the Animal class. The Dog class still implements the makeSound() method, and it still produces a sound that is appropriate for the animal.

If the Cat class violated the contract of the Animal class, then it would not be possible to use a Cat object in place of an Animal object without breaking the code. For example, if the Cat class did not implement the makeSound() method, then it would not be possible to call the makeSound() method on a Cat object. This would break the code that expects an Animal object to be able to make a sound.

The Liskov Substitution Principle is important because it helps to ensure that code is robust and reliable. By following the Liskov Substitution Principle, you can make sure that derived classes can be used in place of their base classes without breaking the code.


*-*

Interface Segregation Principle:
protocol Animal {
  func makeSound()
}

class Dog: Animal {
  func makeSound() {
    print("Woof!")  
  }
}

class Cat: Animal {
  func makeSound() {
    print("Meow!")  
  }
}

let animals: [Animal] = [Dog(), Cat()]
for animal in animals {
  animal.makeSount()
}


In this example, we have an Animal interface and two derived classes: Dog and Cat. The Animal interface has a makeSound() method that makes an animal sound. The Dog and Cat classes override the makeSound() method to make a dog sound and a cat sound, respectively.

This design violates the Interface Segregation Principle because the Animal interface has a method that is not needed by all animals. For example, a cat does not need to make a barking sound.
A better way to design this interface would be to separate the responsibility of making a sound from the responsibility of being an animal. We could do this by creating two interfaces: an Animal interface and a SoundMaker interface. The Animal interface would only be responsible for representing an animal. The SoundMaker interface would only be responsible for making a sound.


*-*

Dependency Inversion Principle:
  • A class should not depend on concrete classes. Instead, a class should depend on abstractions. This will make the class more flexible and easier to test.
protocol Engine {
  func start()
  func run()
}

class GosEngine: Engine {
  func start() {
    print("The gas engine is starting!")  
  }

  func run() {
    print("The gas engine is running!")  
  }
}

class ElectricEngine: Engine {
  func start() {
    print("The electric engine is starting!")  
  }

  func run() {
    print("The electric engine is running!")  
  }
}

class Car {
  var engine: Engine
  
  init(engine: Engine) {
    self.engine = engine
  }

  func drive() {
    engine.start()
    engine.run()
  }
}

let car = Car(engine: GosEngine()) 
car.drive()


In this design, the Car class no longer depends on the GasEngine class. Instead, the Car class depends on the Engine interface (protocol). This means that we can now use any class that implements the Engine interface to create a Car object. For example, we could use an electric engine instead of a gas engine.

This design follows the Dependency Inversion Principle because it decouples the Car class from the Engine class. This makes the Car class more flexible and easier to change. If we want to change the engine, we can change the Engine class without affecting the Car class.


more: https://t.me/software_efficiency


Report Page