The next article will discuss the super important concept is Dependency injection, and how to use it in Spring Boot.
1. Module coupling
1.1. What is Coupling?
First of all, it is necessary to talk briefly about the concept of coupling module . Coupling can be understood as the relationship between two modules, two objects together, with mutual dependencies.
And coupling has two types:
- Tight coupling: two modules are closely linked, difficult to separate
- Loose coupling: weak, discrete bond
In the program, there will usually be many separate modules, each with its own functions, and a relationship (HAS-A in OOP).
For example, two modules Car and Engine , car depend on engine can run. The code is shown as follows.
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">class</span> <span class="token class-name">ChinaEngine</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">private</span> <span class="token class-name">ChinaEngine</span> engine <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Khi tạo Car thì nhớ gắn engine vào :D</span> engine <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ChinaEngine</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> |
1.2. Principle of dependencies
We have just gone through the concept of coupling. Next here are important principles related to it that you need to remember.
In order for the code to be easy to maintain and modify, the principle is to reduce dependencies between modules.
That means turning the relationship between them from tight coupling to loose coupling.
As the code above, the dependency between Car
and ChinaEngine
very strong. This leads to many difficulties:
- Want to change the engine, need to fix the
Car
class - It is impossible to have two
Car
using differentEngine
- It is more difficult to test the modules
In the program there are not only 1, 2 modules like the above example, but there are many. Therefore, if the modules stick too tightly together, it will be difficult to maintain.
2. Dependency inversion principle
2.1. Dependency inversion principle
This is Principle # 5 of SOLID principles, corresponding to the letter D. Designed to design the modules in the program, in such a way that there are as few dependencies as possible.
The DI principle has two main ideas:
- High-level modules should not depend (directly) on low-level modules. Both should depend on abstraction (of OOP).
- Abstraction should not be dependent on details, but vice versa.
Okay, that sounds pretty abstract. Let’s take the analysis slowly, step by step.
First, you need to understand what dependency is. The example above the Car
class depends on the ChinaEngine
class, so ChinaEngine
is a dependency of the Car
. At this point, we say Car
is a high-level module, ChinaEngine
is a low-level module.
2.2. The first point of the DI principle
Reviewing the above code, it can be seen that the code violates point 1 of the DI principle. The reason is because Car
was directly dependent on ChinaEngine
(because the Car
class code uses ChinaEngine
),
To conform to the DI principle, let’s fix it as follows. By making both modules depend on abstraction (in OOP it is usually interface).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token comment">// Interface đại diện cho mọi loại động cơ</span> <span class="token keyword">interface</span> <span class="token class-name">Engine</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 comment">// ChinaEngine là một loại Engine</span> <span class="token keyword">class</span> <span class="token class-name">ChinaEngine</span> <span class="token keyword">implements</span> <span class="token class-name">Engine</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 comment">// Trong Car thì chỉ dùng Engine (chung chung), không có cụ thể loại nào</span> <span class="token comment">// Loại engine cụ thể sẽ được inject vào lúc tạo (không phải gán cứng trong code)</span> <span class="token comment">// Do đó có thể tạo Car với các loại Engine khác nhau</span> <span class="token keyword">class</span> <span class="token class-name">Car</span> <span class="token punctuation">{</span> <span class="token comment">// Loại engine nào đó, lợi dụng tính đa hình OOP</span> <span class="token keyword">private</span> <span class="token class-name">Engine</span> engine <span class="token punctuation">;</span> <span class="token comment">// Khi tạo Car thì tạo Engine object trước, rồi inject vào constructor này</span> <span class="token keyword">public</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> <span class="token class-name">Engine</span> engine <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> engine <span class="token operator">=</span> engine <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The above code takes advantage of the OOP polymorphism to switch between Engine
types without error.
2.3. Code explanation
As above, after editing, both Car
and ChinaEngine
depend on the Engine
interface (representing abstraction). This is true to the principle that DI sets out:
ChinaEngine
is anEngine
type.Engine
has any methods,ChinaEngine
must implement it all. Therefore, when installed in theCar
, the functions of the engine are the same and are in theEngine
.Car
usesEngine
as an engine, instead of a specific class. Therefore, as long as the engine belongs to theEngine
(implement this interface), it can be attached to theCar
.
At this point, the relationship between Car
and ChinaEngine
has been much looser. And we can easily add another type of engine as follows.
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">VNEngine</span> <span class="token keyword">implements</span> <span class="token class-name">Engine</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> |
But then how to use, if there are two types of Engine
, how do I know which one to attach to the Car
?
Yes, but this will be done when instantiating the Car
object. That means when we create a Car
, we attach an Engine
to it. Reviewing the first code, we have ChinaEngine
attached ChinaEngine
to the Car
within the Car
itself, in fact it must be done outside, when creating the Car
. That is the difficulty when the connection between them is too hard.
And as the example code below, when the relationship becomes more loosely, we can create 2 different Car
objects, with two different Engine
types easily.
1 2 3 4 5 6 7 8 | <span class="token comment">// Tạo động cơ trước</span> <span class="token class-name">Engine</span> goodEngine <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VNEngine</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token class-name">Engine</span> cheapEngine <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ChinaEngine</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Tạo xe, khi tạo thì gắn động cơ vào (qua constructor)</span> <span class="token class-name">Car</span> myCar <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> goodEngine <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token class-name">Car</span> yourCar <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Car</span> <span class="token punctuation">(</span> cheapEngine <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// and bad :)</span> |
Above example we implement attach Engine
to Car
in the constructor. This is called constructor-based injection , we will discuss in more detail in the next section.
2.4. The second point of the DI principle
The second point of the DI principle is pretty straightforward if you master OOP. Specifically, the principle “abstraction should not depend on details, but vice versa” means that abstraction only takes the properties, the most common actions, regardless of the details within them.
Returning to the Engine example above, we just need to know how abstraction Engine has a run method, and how the different types of engines perform run (details) do not care.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token comment">// Mọi loại Engine đều có thể run</span> <span class="token keyword">interface</span> <span class="token class-name">Engine</span> <span class="token punctuation">{</span> <span class="token keyword">void</span> <span class="token function">run</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Động cơ VNEngine run theo hiểu khác</span> <span class="token keyword">class</span> <span class="token class-name">VNEngine</span> <span class="token keyword">implements</span> <span class="token class-name">Engine</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Run nhanh, bền, ít tốn xăng</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Động cơ ChinaEngine run theo kiểu khác</span> <span class="token keyword">class</span> <span class="token class-name">ChinaEngine</span> <span class="token keyword">implements</span> <span class="token class-name">Engine</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Run nhanh, bền nhưng tốn xăng</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Then inside the Car
class, it doesn’t care how the engine runs. It just needs to know that when doing some actions, the car will run, that’s all.
Part 1 of this article is over. Please look forward to the next part. Have any questions, please comment below, because this is quite an important part of learning Spring.