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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">class</span> <span class="token class-name">Handler</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">handle</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token function">requestDataToAPI</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">let</span> array <span class="token operator">=</span> <span class="token function">parse</span> <span class="token punctuation">(</span> data <span class="token punctuation">:</span> data <span class="token punctuation">)</span> <span class="token function">saveToDB</span> <span class="token punctuation">(</span> array <span class="token punctuation">:</span> array <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function-definition function">requestDataToAPI</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Data</span> <span class="token punctuation">{</span> <span class="token comment">// send API request and wait the response</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function-definition function">parse</span> <span class="token punctuation">(</span> data <span class="token punctuation">:</span> <span class="token class-name">Data</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">[</span> <span class="token class-name">String</span> <span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token comment">// parse the data and create the array</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function-definition function">saveToDB</span> <span class="token punctuation">(</span> array <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token class-name">String</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// save the array in a database (CoreData/Realm/...)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">class</span> <span class="token class-name">Handler</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> apiHandler <span class="token punctuation">:</span> <span class="token class-name">APIHandler</span> <span class="token keyword">let</span> parseHandler <span class="token punctuation">:</span> <span class="token class-name">ParseHandler</span> <span class="token keyword">let</span> dbHandler <span class="token punctuation">:</span> <span class="token class-name">DBHandler</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> apiHandler <span class="token punctuation">:</span> <span class="token class-name">APIHandler</span> <span class="token punctuation">,</span> parseHandler <span class="token punctuation">:</span> <span class="token class-name">ParseHandler</span> <span class="token punctuation">,</span> dbHandler <span class="token punctuation">:</span> <span class="token class-name">DBHandler</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> apiHandler <span class="token operator">=</span> apiHandler <span class="token keyword">self</span> <span class="token punctuation">.</span> parseHandler <span class="token operator">=</span> parseHandler <span class="token keyword">self</span> <span class="token punctuation">.</span> dbHandler <span class="token operator">=</span> dbHandler <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">handle</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> apiHandler <span class="token punctuation">.</span> <span class="token function">requestDataToAPI</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">let</span> array <span class="token operator">=</span> parseHandler <span class="token punctuation">.</span> <span class="token function">parse</span> <span class="token punctuation">(</span> data <span class="token punctuation">:</span> data <span class="token punctuation">)</span> dbHandler <span class="token punctuation">.</span> <span class="token function">saveToDB</span> <span class="token punctuation">(</span> array <span class="token punctuation">:</span> array <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">class</span> <span class="token class-name">APIHandler</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">requestDataToAPI</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Data</span> <span class="token punctuation">{</span> <span class="token comment">// send API request and wait the response</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">ParseHandler</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">parse</span> <span class="token punctuation">(</span> data <span class="token punctuation">:</span> <span class="token class-name">Data</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">[</span> <span class="token class-name">String</span> <span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token comment">// parse the data and create the array</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">DBHandler</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">saveToDB</span> <span class="token punctuation">(</span> array <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token class-name">String</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// save the array in a database (CoreData/Realm/...)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <span class="token keyword">class</span> <span class="token class-name">Logger</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">printData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> cars <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Batmobile"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Black"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"SuperCar"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Gold"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"FamilyCar"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Grey"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> cars <span class="token punctuation">.</span> forEach <span class="token punctuation">{</span> car <span class="token keyword">in</span> <span class="token function">print</span> <span class="token punctuation">(</span> car <span class="token punctuation">.</span> <span class="token function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Car</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> name <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">let</span> color <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> name <span class="token operator">=</span> name <span class="token keyword">self</span> <span class="token punctuation">.</span> color <span class="token operator">=</span> color <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string-literal"><span class="token string">"I'm </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">name</span> <span class="token interpolation-punctuation punctuation">)</span><span class="token string"> and my color is </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">color</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <span class="token keyword">class</span> <span class="token class-name">Logger</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">printData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> cars <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Batmobile"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Black"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"SuperCar"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Gold"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"FamilyCar"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Grey"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> cars <span class="token punctuation">.</span> forEach <span class="token punctuation">{</span> car <span class="token keyword">in</span> <span class="token function">print</span> <span class="token punctuation">(</span> car <span class="token punctuation">.</span> <span class="token function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> bicycles <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token class-name">Bicycle</span> <span class="token punctuation">(</span> type <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"BMX"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Bicycle</span> <span class="token punctuation">(</span> type <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Tandem"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> bicycles <span class="token punctuation">.</span> forEach <span class="token punctuation">{</span> bicycles <span class="token keyword">in</span> <span class="token function">print</span> <span class="token punctuation">(</span> bicycles <span class="token punctuation">.</span> <span class="token function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Car</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> name <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">let</span> color <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> name <span class="token operator">=</span> name <span class="token keyword">self</span> <span class="token punctuation">.</span> color <span class="token operator">=</span> color <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string-literal"><span class="token string">"I'm </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">name</span> <span class="token interpolation-punctuation punctuation">)</span><span class="token string"> and my color is </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">color</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Bicycle</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> type <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> type <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> type <span class="token operator">=</span> type <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string-literal"><span class="token string">"I'm a </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">type</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <span class="token keyword">protocol</span> <span class="token class-name">Printable</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">String</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Logger</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">printData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> cars <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token class-name">Printable</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Batmobile"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Black"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"SuperCar"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Gold"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"FamilyCar"</span></span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Grey"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Bicycle</span> <span class="token punctuation">(</span> type <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"BMX"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">Bicycle</span> <span class="token punctuation">(</span> type <span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"Tandem"</span></span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> cars <span class="token punctuation">.</span> forEach <span class="token punctuation">{</span> car <span class="token keyword">in</span> <span class="token function">print</span> <span class="token punctuation">(</span> car <span class="token punctuation">.</span> <span class="token function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Car</span> <span class="token punctuation">:</span> <span class="token class-name">Printable</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> name <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">let</span> color <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> name <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">,</span> color <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> name <span class="token operator">=</span> name <span class="token keyword">self</span> <span class="token punctuation">.</span> color <span class="token operator">=</span> color <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string-literal"><span class="token string">"I'm </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">name</span> <span class="token interpolation-punctuation punctuation">)</span><span class="token string"> and my color is </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">color</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Bicycle</span> <span class="token punctuation">:</span> <span class="token class-name">Printable</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> type <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> type <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> type <span class="token operator">=</span> type <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">printDetails</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string-literal"><span class="token string">"I'm a </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">type</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <span class="token keyword">class</span> <span class="token class-name">Rectangle</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> width <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token keyword">var</span> height <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> width <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token punctuation">,</span> height <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> width <span class="token operator">=</span> width <span class="token keyword">self</span> <span class="token punctuation">.</span> height <span class="token operator">=</span> height <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">area</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Int</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> width <span class="token operator">*</span> height <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Square</span> <span class="token punctuation">:</span> <span class="token class-name">Rectangle</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">var</span> width <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token punctuation">{</span> <span class="token keyword">didSet</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> height <span class="token operator">=</span> width <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">var</span> height <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token punctuation">{</span> <span class="token keyword">didSet</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> width <span class="token operator">=</span> height <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
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:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">func</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> square <span class="token operator">=</span> <span class="token class-name">Square</span> <span class="token punctuation">(</span> width <span class="token punctuation">:</span> <span class="token number">10</span> <span class="token punctuation">,</span> height <span class="token punctuation">:</span> <span class="token number">10</span> <span class="token punctuation">)</span> <span class="token keyword">let</span> rectangle <span class="token punctuation">:</span> <span class="token class-name">Rectangle</span> <span class="token operator">=</span> square rectangle <span class="token punctuation">.</span> height <span class="token operator">=</span> <span class="token number">7</span> rectangle <span class="token punctuation">.</span> width <span class="token operator">=</span> <span class="token number">5</span> <span class="token function">print</span> <span class="token punctuation">(</span> rectangle <span class="token punctuation">.</span> <span class="token function">area</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">// As a rectangle we should expect the area as 7 x 5 = 35, but we got 5 x 5 = 25</span> <span class="token punctuation">}</span> |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <span class="token keyword">protocol</span> <span class="token class-name">Geometrics</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">area</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Int</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Rectangle</span> <span class="token punctuation">:</span> <span class="token class-name">Geometrics</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> width <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token keyword">var</span> height <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> width <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token punctuation">,</span> height <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> width <span class="token operator">=</span> width <span class="token keyword">self</span> <span class="token punctuation">.</span> height <span class="token operator">=</span> height <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">area</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Int</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> width <span class="token operator">*</span> height <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Square</span> <span class="token punctuation">:</span> <span class="token class-name">Geometrics</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> edge <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> edge <span class="token punctuation">:</span> <span class="token class-name">Int</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> edge <span class="token operator">=</span> edge <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">area</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Int</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> edge <span class="token operator">*</span> edge <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> rectangle <span class="token punctuation">:</span> <span class="token class-name">Geometrics</span> <span class="token operator">=</span> <span class="token class-name">Rectangle</span> <span class="token punctuation">(</span> width <span class="token punctuation">:</span> <span class="token number">10</span> <span class="token punctuation">,</span> height <span class="token punctuation">:</span> <span class="token number">5</span> <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> rectangle <span class="token punctuation">.</span> <span class="token function">area</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">// 10*5 = 50</span> <span class="token keyword">let</span> rectangle2 <span class="token punctuation">:</span> <span class="token class-name">Geometrics</span> <span class="token operator">=</span> <span class="token class-name">Square</span> <span class="token punctuation">(</span> edge <span class="token punctuation">:</span> <span class="token number">5</span> <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> rectangle2 <span class="token punctuation">.</span> <span class="token function">area</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">// 5*5 = 25</span> <span class="token punctuation">}</span> |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <span class="token keyword">protocol</span> <span class="token class-name">GestureProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token class-name">Sau</span> <span class="token number">1</span> thời gian chúng ta làm việc <span class="token punctuation">,</span> thì <span class="token keyword">protocol</span> này phình to ra <span class="token punctuation">.</span> <span class="token class-name">Nh</span> ư sau <span class="token punctuation">:</span> <span class="token keyword">protocol</span> <span class="token class-name">GestureProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function-definition function">didDoubleTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function-definition function">didLongPress</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token class-name">Class</span> <span class="token class-name">SuperButton</span> của chúng ta cần cả <span class="token number">3</span> hàm trên <span class="token punctuation">,</span> nên nó sẽ đúng khi chúng ta <span class="token function">inform</span> <span class="token punctuation">(</span> kế thừa <span class="token punctuation">)</span> <span class="token class-name">SuperButton</span> với <span class="token class-name">GestureProtocol</span> <span class="token keyword">class</span> <span class="token class-name">SuperButton</span> <span class="token punctuation">:</span> <span class="token class-name">GestureProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send tap action</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">didDoubleTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send double tap action</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">didLongPress</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send long press action</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token class-name">Tuy</span> nhiên <span class="token punctuation">,</span> bắt đầu phát sinh <span class="token number">1</span> vấn đề khác là <span class="token punctuation">:</span> chúng ta có <span class="token number">1</span> <span class="token keyword">class</span> <span class="token class-name">PoorButton</span> <span class="token punctuation">.</span> <span class="token class-name">V</span> à <span class="token keyword">class</span> này chỉ cần duy nhất <span class="token number">1</span> hàm <span class="token function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">class</span> <span class="token class-name">PoorButton</span> <span class="token punctuation">:</span> <span class="token class-name">GestureProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send tap action</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">didDoubleTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">didLongPress</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <span class="token keyword">protocol</span> <span class="token class-name">TapProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token class-name">DoubleTapProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didDoubleTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token class-name">LongPressProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didLongPress</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">SuperButton</span> <span class="token punctuation">:</span> <span class="token class-name">TapProtocol</span> <span class="token punctuation">,</span> <span class="token class-name">DoubleTapProtocol</span> <span class="token punctuation">,</span> <span class="token class-name">LongPressProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send tap action</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">didDoubleTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send double tap action</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">didLongPress</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send long press action</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">PoorButton</span> <span class="token punctuation">:</span> <span class="token class-name">TapProtocol</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">didTap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// send tap action</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The Dependency Inversion Principle (DIP)
- High level modules should not depend upon low level modules. both should depend on abstractions.
- 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.