Discussing with various people - most of them experienced developers - a classic work “Object-oriented design techniques. Design Patterns Gamma, Helm, and others, I was amazed to find a complete misunderstanding of one of the basic OOP approaches — the distinction between classes and interfaces.
To the authors of the book, this question seems so transparent that they devote only two pages to it, assuming that all this should be obvious to readers. And, really, this did not cause me any questions - it seemed so
self-evident that when I met several programmers some time ago with a frank misunderstanding of the concept, I could not even find the words to explain its essence.
')
Therefore, I tried to systematize my understanding of the issue in this article.
The main difference between a class and an interface is that a
class consists of an interface and an implementation .
Any class always implicitly declares its interface - what is available when using the class from the outside. If we have a class Key and it has a public method Open, which calls the private methods Insert, Rotate and Remove, then the interface of the class Key consists of the method Open. When we inherit a class from the Key class, it will inherit this interface.
In addition to this interface, the class also has an implementation — the Insert, Rotate, Take out methods and their call in the Open method. Key Heirs inherit along with the interface and implementation.
And here lurk problems. Suppose we have a certain model that involves using a key to open the door. It knows the Key interface and therefore calls the Open method.
But suppose that some doors do not open with such a turning key, but with a magnetic card - which is also essentially a key! The interface of this card is not fundamentally different from the interface of a regular key - you can open with a key, or you can open with a card.
And we want to make a class Magnetic Card, which will also contain the Key interface. For this, we will inherit the Magnetic Card from the Key. But along with the interface, the implementation was also inherited, which in the Open method calls Insert, Rotate and Remove methods - and this is completely inappropriate for the Magnetic Card.
We will have to overload the implementation of the Open method in the Magnetic Card at the very least, using the Insert, Draw and Draw sequence already. This is already bad, because we don’t know the details of the implementation of the Key class - what if we missed some very important data change that needed to be made - and was made in the Key :: Open method? We will have to go into the internal implementation of the Key and look at what and how - even if we have such technical capability (open source forever and so on), this is a gross violation of encapsulation, which will not lead to anything good.
This is what Gamma et al. Write:
inheritance is a violation of encapsulation .
You can try to think about such questions yourself:
- What to do with the fact that the Key is inserted simply into the well, and the Magnetic Card is necessarily on top (not in the middle and not below)?
- What to do when we need to make a Contactless Card, which should not be inserted, and bring?
The correct approach to this issue is
to separate the interface from the implementation . Many modern languages have a special syntax for this. In backward languages, you can use hacks, for example, in Tse-two-crosses - purely abstract classes and multiple inheritance (sorry for obscene vocabulary - you can't throw a word out of a song).
We must rely on interfaces, not classes.Declare interface Key containing Open method.
We declare the Rotation Key class that implements the Key interface using our Insert, Rotate, and Take methods.
Let's declare a class Magnetic Card, which also implements the Key interface, but in its own way - and without any unpleasant intersections with the implementation of the Rotary Key. This helped us achieve separation of the interface from implementation.
Always keep in mind the general principle: we need to use not an interface, but an interface. It does not matter to us what this thing is - a swivel key or a magnetic card - it is important for us that they can open the door. That is, instead of thinking about the nature of the object, we think about how to use it.
It may seem strange to you, but
this is exactly what distinguishes man from animals — the use of interfaces instead of classes. You probably remember the classic experience with the monkey, which was taught to extinguish the fire with water from a bucket; and then they put the bucket on the raft in the middle of the pool, but the monkey still ran along the bridge to the raft and scooped water from the bucket, instead of drawing water directly from the pool. That is, the monkey used the Water-in-Bucket class instead of the Water interface (and even more, I will tell you a secret: instead of the interface, the Means for Quenching).
When we think in classes, we become like animals. People think (and program) interfaces.
The use of interfaces gives great opportunities. For example, a class can implement several interfaces: the Key-from-the-Doorphone class can contain the Key and Keyfob interfaces.
As for class inheritance, which, as you remember, breaks encapsulation — often,
instead of inheritance, it is better to use delegation and composition . Have you forgotten the Contactless Card? So you want to make it a sister Magnetic Card! (For example, to know that both of them can be put into the Card-to-Card compartment in the Wallet.) However, they seem to have nothing in common: the Key interface unites them in the same way as the Rotary Key with the Key-From-Doorphone .
Decision? We are making a class (and maybe an interface - think what is better here) Card that implements the interface Key by delegating a Magnetic Card or Contactless Card. And to find out what delegation and composition are, as well as what an abstract factory is about - refer to the books on design patterns.
There are many more advantages to using interfaces instead of classes. You will be able to see them in practice. Stay human!
PS To my surprise, I could not find a blog dedicated to OOP or a blog dedicated to programming in general at Habrahabr. If there are any, please indicate, but for now, in the absence of the best options, I place it on a personal blog.