Interface Segregation Principle is a principle that you need to remember when defining interfaces so that they are as small as possible but still ensure to cover all the needs that we want. Avoid the fact that another class implements your interface but has no need to implement some of the methods of this interface in detail because, in the context of this class, these methods have no meaning.
For example, I have an interface that defines some activities of a human as follows:
1 2 3 4 5 6 7 |
package com.huongdankotlin.designpattern.isp interface Human { fun eat() fun run() fun cook() } |
If we implement this interface for newborn objects, we won’t need to implement run() and cook() methods because they can’t have these operations. In this case, either we will have to still add implementation for run() and cook() methods but no content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdankotlin.designpattern.isp class Baby : Human { override fun eat() { println("Baby is eating ...") } override fun run() { } override fun cook() { } } |
or we will throw an exception when the user uses these methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdankotlin.designpattern.isp class Baby : Human { override fun eat() { println("Baby is eating ...") } override fun run() { throw Exception("No running implementation"); } override fun cook() { throw Exception("No cooking implementation"); } } |
This will confuse the user because even though they can call the run() and cook() methods on the Baby object, it’s nothing to them. We need to avoid it!
We can subdivide the Human interface into a number of other interfaces, for example:
1 2 3 4 5 |
package com.huongdankotlin.designpattern.isp interface Runner { fun run() } |
and:
1 2 3 4 5 |
package com.huongdankotlin.designpattern.isp interface Cooker { fun cook() } |
The interface is now only:
1 2 3 4 5 |
package com.huongdankotlin.designpattern.isp interface Human { fun eat() } |
Humans, everyone must eat! 😀
Then, our Baby class only needs to implement the eat() method:
1 2 3 4 5 6 7 |
package com.huongdankotlin.designpattern.isp class Baby : Human { override fun eat() { println("Baby is eating ...") } } |
If you need to implement the run() and cook() methods for the student object, you just need to implement the Runner and Cooker interfaces for this student object:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdankotlin.designpattern.isp class Student : Human, Cooker, Runner { override fun cook() { println("Student is cooking ...") } override fun eat() { println("Student is eating ...") } override fun run() { println("Student is running ...") } } |
So we have separated the Human interface into smaller, more meaningful interfaces. The code is also clearer, isn’t it?