The Dependency Inversion Principle is a coding principle that by definition, high-level modules should not depend on low-level modules, they should only depend on abstraction and the interface should not depend on implementation but vice versa. It will be a bit difficult to imagine for beginners, but I can say it like this: classes that want to get information from other classes, for example, get information from the database, should go through an interface or an abstract class. The implementation of this interface or abstract class will take care of getting the information for you. And of course, the implementation should only depend on the interface, should not vice versa!
For example, I have an application that collects student information. If you do not apply the Dependency Inversion Principle, you can call it directly to get the following information:
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 |
package com.huongdankotlin.designpattern.dip fun main() { val studentRepository: StudentRepository = StudentRepository(); val student = studentRepository.findById(1); println(student?.name) } class StudentRepository() { var students: List<Student> = ArrayList() init { students = arrayListOf(Student(1, "Khanh"), Student(2, "Quan")) } fun findById(id: Int): Student? { for (student: Student in this.students) { if (student.id == id) { return student } } return null } } class Student(var id: Int, var name: String) { } |
Results when running the example:
Try to imagine and think, if now I need to get student information from the database, do I need to rewrite all the code?
To avoid this, and easily change the needs if desired, we should use the interface to get student information as follows:
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 |
package com.huongdankotlin.designpattern.dip fun main() { val studentRepository: StudentRepository = StudentRepo1(); val student = studentRepository.findById(1); println(student?.name) } interface StudentRepository { fun findById(id: Int): Student? } class StudentRepo1(): StudentRepository { private var students: List<Student> = ArrayList() init { students = arrayListOf(Student(1, "Khanh"), Student(2, "Quan")) } override fun findById(id: Int): Student? { for (student: Student in this.students) { if (student.id == id) { return student } } return null } } class Student(var id: Int, var name: String) { } |
Here, I have introduced a StudentRepository interface with the findById() method, and StudentRepo1 class is the class that implements this interface. In the future, if you want to get student information from the database, for example, you just need to create a new class that implements this StudentRepository interface. Our code will not need much modification and still ensure the correctness of the old code.