Developing methods of class for other classes to use, that is interface. Interfaces should not be changed time by time. Because that is knowledge which the other classes know about this class. Those are also connections.
Separating interface from implementation equals separating what rarely change from what change regularly. Interfaces should be same. But implementation is not. In other words, the knowledge that other classes know about class A should be same. Not exact same but at least those are not changed. Regarding to Open/Close principle we close for changing and open for expanding. Changing and expanding are different.
For example, we have Watch class like below.
class Watch
def show_time
end
private
def show_numbers
end
def show_timekeeper
end
end
show_time is interface method. Other classes know that we can show time using Watch. That means any Watch’s instances which are able to show time. If this system has been running for many years and one day we change show_time to time_show. But show_time is a message that other classes would send to an instance of this class. Now it becomes wrong message. That is bad impact if we don’t close for changes. We are able to handle it well but how about big system with a bunch of connections? We even can not do this or going crazy by doing this. It costs time and taking risk.
So we should not change this part. This is interface. We have to look for a method name which is in common, like a plug, an interface for many things. That recalls us about abstract level.
class Watch
def show_time
end
end
It’s necessary to say that Naming is very important, especially name for interfaces which should not be changed in future. Every time we code, let’s think about interfaces.
- What interfaces are we working on?
- Are we putting implementation to interface?
- Are we changing interface methods?
- What are impacts and boundaries?
Getting back to above code, what we should do if we want to change how it does but not what it is? . Look! WHAT IT IS? is usually about Interface. HOW IT DOES? is the implementation. Implementation is a sign for a thing which change regularly. Programing to interface does not mean we put a bunch of code to interface methods. But that means we uses Interfaces as the main messages, exactly INTERFACE, a port or an electric plug. Implementation would being used by putting code to these interfaces.
Look at above electric plug! People (other classes) don’t know exactly where it would go. They have no idea about the electric cables behind which are new or not.
- WHAT IT DOES? – providing electricity.
- HOW IT DOES? – no idea! The person who installed this would know. This plug can be moved to another house and being reused. Interface is same but another implementation (in another house).
That is way of critical thinking in coding and design.
According to above explanation we can implement like this.
class ClassicSkin
def show
end
end
class Watch
attr_reader :skin
def initialize(skin)
@skin = skin
end
def show_time
skin.show
end
end
Now ClassicSkin becomes the implementation of show_time interface. show method is also other interface method which belongs to ClassicSkin. But it can be the implementation for show_time interface method. Or another implementation level here:
class WatchSkin
def show_classic
end
def show_digital
end
end
Feel free to change show_time implementation by involving different method. By using first solution we can have two classes:
- ClassicSkin
- DigitalSkin
So we use Dependency Injection because this instance can be changed regularly.
However, with second solution, we have only one class WatchSkin with different methods so we even don’t need to create instance. We use Isolation by private method.
class Watch
def show_time
skin.show_classic
end
def skin
@skin ||= WatchSkin.new
end
end
We use DI because skin changed regularly whenever creating object – it created in constructor initialize method. Thus, we get to know the implementation might change regularly so we should separate them from interface. In order to do that, we have to know what exactly interface and where they are in code.
Conclusion
When we write code, we should care for other developers who will read our code. We make them please by not forcing them to read the implementation which is function body. They only read the class or method name to get to know what it does and take use of it.
In order to limit changes in future, we saparate what might be changed from what stay the same. We need to predict that. For exam, the Watch’s skin might be changed but show_time does not. So we move skin’s interface out of Watch class.
Thank for reading!