SOLID are principles that lead you to write great code without additional effort.
With great application comes great responsibility. It means that the code base should be flexible, expandable without much effort, and easy to test. SOLID principles are the exact things that let you support the top level of quality.
In this article, I'll explain the meaning of each principle.
Five SOLID principles:
Single responsibility
There should never be more than one reason for a class to change.
In other words, every class should have only one responsibility. Each class needs to have a focused, specialized role.
This is how the Single Responsibility principle is violated. We can’t use a single class for presenting UI and Network requests. Instead of this, we should use another class for Network requests.
Open-closed
Software entities should be open for extension but closed for modification.
An entity can allow its behavior to be extended without modifying its source code.
For example, it should be possible to add fields to the data structures it contains, or new elements to the set of functions it performs.
A module will be said to be closed if it is available for use by other modules. This assumes that the module has been given a well-defined, stable description (the interface in the sense of information hiding).
Hotly disputed principle as it is not clear how to use this one. Someone provides an example of using an interface type instead of a particular class type. But it is the dependency inversion principle.
Instead of it, let's use protocols extensions. For example:
So we want to change the mine
function without changing FoxMiner class realization. Let’s create two protocols: DiamondMinerProtocol and RubyMinerProtocol.
Now we will create extensions for these protocols:
Now let's return to our FoxMiner class:
Now, if we want FoxMiner class realization, then we use FoxMiner type variable or variable without type:
If we change variable fox type to DiamondMinerProtocol type:
And RubyMinerProtocol:
Also, you can change function call without creating a new instance of the class:
That's how this principle could be used in Swift.
Liskov substitution
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
Liskov substitution is based on the concept of "substitutability"—a principle in object-oriented programming stating that an object (such as a class) and a sub-object (such as a class that extends the first class) must be interchangeable without breaking the program.
This principle is often used in iOS development as we love to use BaseClasses.
For example, we have an app with a similar design on every screen. This means we can create some BaseClass that will set up all UI, and we will just inherit from that BaseClass instead of copy-pasting the same code in every ViewController.
Interface segregation
Clients should not be forced to depend upon interfaces that they do not use.
The interface segregation principle (ISP) splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them. Such shrunken interfaces are also called role interfaces. ISP is intended to keep a system decoupled and thus easier to refactor, change, and redeploy.
This is too easy — just don't make supermassive protocols (interfaces).
Dependency Inversion
Depend upon abstractions, not concretions.
When following this principle, the conventional dependency relationships established from high-level policy-setting modules to low-level dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details.
I've already mentioned this principle in open-closed. This one is very useful when we are working with third-party libraries. For example, we have FirebaseAnalytics for events logging, and after some time, we decide to change it to AppDynamics. To make it less painless, we should use this principle.
But AppDynamics has an API that is different from Firebase one. So instead of changing log events everywhere, we should use abstraction:
And implementation:
Now in our app, we will send events through the adapter:
If we will move from FirebaseAnalytics to AppDynamics, it means that changes will be only in EventsServiceAdapter.
Conclusion
In this article, I've tried to describe all 5 principles of SOLID and given examples. SOLID principles are also great to use when you're creating app architecture. These principles should be used for simplifying your code life, not making it hell.
If you need more expertise in developing and supporting iOS applications, you can also get free consultation. Mad Devs is always ready to provide best mobile app development practices.