Applying SOLID principles in iOS

Tram Ho

1. Overview

SOLID stands for the first 5 letters of the 5 principles of object-oriented design. Help programmers write code that is easy to read, understand, and maintain. It was given by Robert C. Martin and Michael Feathers. Those 5 principles include:

  • Single responsibility principle (SRP)
  • Open/Closed principle (OCP)
  • Liskov substitution principe (LSP)
  • Interface segregation principle (ISP)
  • Dependency inversion principle (DIP)

2. Principles

The Single Responsibility Principle (SRP)

There should never be more than one reason for a class to change. In other words, every class should have only one responsibility.

This principle says that each class should only be responsible for a specific task.

So every time we create/edit a class. Always wonder how many roles have been taken in this class?

See the following example:

After you read the above code, how much work is the above class responsible for?

The Handler class is responsible for getting data from API (1), parsing data into string array type (2) and saving data to database (3). Applied to real projects, we use Alamofire to call API (1), ObjectMapper to parse data (2) and use Core Data to save data to database (3). By then, the code in the example will become cumbersome, difficult to maintain and extend.

Treatment:

This principle helps your class to be as clean as possible. Also, in the first example, you cannot test requestDataToAPI , parse and saveToDB directly, since they are private methods. After modifying the code, we can easily test these functions.

The Open-Closed Principle (OCP)

Software entities … should be open for extension, but closed for modification.

The principle says that we should not modify an existing class, only extend it.

If you want to create a class that is easy to maintain, two important conditions must be met:

  • Open for extension: You can easily add and change the behavior of that class easily.
  • Close for modification: The existing behavior of the class cannot be changed.

We have the following example, the Logger class is responsible for printing out the details of the Car . classes

So if we want the Logger class to print more details of another new class, then we have to change the printData() function each time. This violates the OCP principle we are introducing.

For example, I will add a class Bicycle. Everyone will find themselves having to change the printData() function of the Logger class

Workaround: We will create a Printable protocol, the vehicle classes (Car, Bicycle) will conform to this protocol. The function printData() will print an array of Printable

That way, we have created an extra layer of abstraction between printData() and the class that needs to print the data. Allows adding new classes (eg Bicycle) without having to change the code in the printData() function

The Liskov Substitution Principle (LSP)

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it

This principle says: if class A is a subclass of class B. then the functions in class A must perform the same actions as class B. To understand this principle better. Let’s see the following example:

We have class Rectangle and a function to calculate area (length times width), class Square because it is a square, so we have length equal to width.

Let’s see how to calculate the area of ​​the Square class:

According to the principle of LSP, because class Square inherits from class Rectangle . So the area() function should always be equal to the length times the width (here 7×5 = 35). However, we get an area of ​​25. Therefore, the above example violates this LSP principle.

Solution: We use the Geometrics protocol which contains the area function. So the Square class no longer inherits from the Rectangle class. But both of these classes inherit from protocol.

The Interface Segregation Principle (ISP)

Many client-specific interfaces are better than one general-purpose interface.

This principle introduces one of the problems of object-oriented programming: the fat interface. The interface is too big, which means that in that interface (in iOS programming can understand the interface as a protocol) there are too many functions/properties. It contains a lot of unnecessary information. See the following example:

We have protocol GestureProtocol with didTap() function

So this PoorButton class omits 2 functions didDoubleTap() and didLongPress() , which means we have passed 2 unused functions to the PoorButton class. Thus violating the ISP principle we are talking about here.

Workaround: Expose these functions to individual protocols. And only transmit the necessary protocols.

The Dependency Inversion Principle (DIP)

  1. High level modules should not depend upon low level modules. both should depend on abstractions.
  2. Abstractions should not depend on details. details should depend upon abstractions.

This principle is quite important for programmers. And it is also related to Dependency Injection (DJ). So what is Dependency Inversion (DI)? And what is a DJ? What is the relationship between DI and DJ? I will introduce in the next blog post.

3. Conclusion

If you follow the SOLID principle, you can increase the quality of your code. Our code will become more readable, maintainable, and extensible for the future. However, to apply SOLID principles to real projects will be more difficult. But for that, we should master this foundational knowledge so that we can apply and modify it when doing real projects.

Share the news now

Source : Viblo