Interface and inheritance - an object-oriented dilemma

I had problems understanding when / why to implement inheritance and when / why to implement inheritance through the interface. Please carry me as I explain.

Let's say we have a parent class Animal , and we want to extend it with 3 subclasses: Dog , Cat and Mouse .

Suppose all Animals can eat() , sleep() , scratch() and move() . and Dog can pant() . With this knowledge, we will continue to add the first 4 behaviors to the Animal superclass and have Dog , Cat and Mouse extend Animal . We will also add the pant() method to the Dog class, but only the pant() dogs.

Now, what happens if we want to add another method called waggleTail() , but only Cat and Dog exhibit this behavior. We cannot add this behavior to Animal bec, then Mouse also inherits the behavior (and the mouse does not flap its tail). An alternative is to add the waggleTail() method to the Dog and Cat classes, but not to the Mouse class. This approach, however, does not make sense, because we would then violate the DRY principle (do not repeat yourself) by using the waggleTail() method waggleTail() . We want to write each method once and only once.

Perhaps we could solve this problem by creating a new subclass inherited from Animal called TailWagglingAnimal , add the waggleTail() method to this subclass, and then inherit from this Dog and Cat subclass. This sounds reasonable until you realize that there are many other similar anomalies, and we will have to repeat this process again and again for each of them (this will not lead to an expansion of the inheritance hierarchy).

Also, what if we have a specific type of Dog (let's call it "Coton de Tulear") that demonstrates all of Dog other behaviors (for example, suffocating), except that he does not shake his tail. If we have a "Coton de Tulear" inherited directly from Animal , it will not be able to suffocate (). If we inherited it from Dog , it would be able to flap its tail (bec Dog extends TailWagglingAnimal ). If we had Dog extend Animal directly and then create a new subclass called TailWagglingDog (like appose to TailWagglingAnimal ), Cat would not be able to inherit this behavior (so we would need to duplicate the behavior somewhere inside the Cat hierarchy, which violates the DRY principle) .

What should we do?

Based on dozens of threads in stackoverflow (and several OO design books), it was suggested to remove the waggleTail() method from the Dog class and add it to the interface and interface. Let’s call the TailWaggler interface, and then all dogs (except "Coton de Tulear") implement this interface. However, it’s hard for me to understand why / how it is useful.

If you think about it, this means that all 50+ dog loaves (suppose there are 50 dog loaves that should exhibit this behavior) you need to add the TailWaggler tools the keyword in the form of only one species Dog does not exhibit such behavior. Not only does this mean a lot of additional manual work on the part of the programmer (adding TailWaggler tools to the beginning of each class), it means that all descendants should be connected with all the small and minor details of the behavior that they (this would not be so if we would add this behavior to the parent class and extend the parent class). It might be good if we had only a few such cases, but what if we had dozens or hundreds of such cases? Finally, as we add new types of subclasses of dog types, in the end there will be one type of Dog - another that will not demonstrate one of the actions of the parent class of Dog, so this means slowly, but of course, we will need to remove almost all the behavior from the class (parent) Dog and add them to the interface? Then we need to make sure that all subclass implement dozens of different interfaces. It can be assumed that we group all the behaviors associated with this in one interface, but this is only possible if the behavior shown by different dogs is uniform - what if this is not so?)

Thanks!

+5
source share
4 answers

Then we need to make sure that all subclasses implement dozens of different interfaces

  • If your class needs to implement too many interfaces, make sure that it does not violate the principle of single responsibility. Try breaking the class into smaller ones.

  • The implementation of several small interfaces instead of the large one corresponds to the principle of interface separability, which leads to some positive consequences.

this means that all descendants must take care of all the small and small details of the behavior that they exhibit.

This is more due to implementation difficulties. Multiple inheritance or automatic delegation can help here. Since we have neither in Java, we must choose between other parameters:

+3
source

Inheritance is used if you want to morph a class that has the same type of your parent class and that has similar behavior.
An interface is used to declare the functionality of your class.

For example, dogs, cats and mice are all animals (the same types), and they have similar behavior (they are born, grow, die, move, eat, ...). This way your Dog class can extend Animal.

Now your interfaces declare their functions. As we just saw, an animal can move and eat, so your Animal class can implement Mover and Eater interfaces, for example. Automatically, a dog, cat, and mouse inherit these interfaces, but the mouse will eat cheese, and the dogs and cats will eat meat. Here you can @Override (understand morphing ) implementation behavior to declare what each class can eat.

If another animal can rise (monkey), you implement the Climber interface directly in the Monkey class. This is slightly improved than the standard Animal.


For your tail problem, you need to implement the Tailwagger interface in Dog and Cat, not Animal (all animals are not tailwaggers). Of course, you do not want to repeat the code, so you will also create another class called StandardTailwag and use it as a field in Dog and Cat ( composition ). Then you need to redirect the implementation to the methods of this class, but this is the way to go if you want your code to be simplified in the future.

You can also convert StandardTailwag to DogTailwag and CatTailwag and easily implement them using a single Tailwagger interface


Please note that you can write default code and methods in Java 8 interfaces, but this is not recommended.

+2
source

This is a very broad and subjective question, so I can give you my opinion and nothing more.

My personal principle: “The less code you write, the better”, and being truthful to achieve simplicity is extremely difficult.

I am trying to make inheritance as small as possible, since deep inheritance tends to become a problem later when your model changes.

Then I use the interface with the handlers, so instead of having the waggleTail method, I have a stateless TailWaggler class that does the hanging thing.

I don’t think there is a recipe for every possible situation, I try to keep it as simple as possible, as much as possible, then you will (sooner or later) reorganize your code, if the test is good, it will not be too painfully.

+2
source

A short answer to a long question, so do not take it until others who have no more energy turn on. BUT, as I would do, it would have an abstract Dog class that implements the TailWagger interface and has a specific tailWag function.

Then all your dogs will inherit the Dog, including the one that doesn’t actually wag. Then, in this specific implementation of the dog, create a new specific function called tailWag that throws an exception on the lines InvalidStateException ("This type of dog does not wag its tail").

Using this methodology, you have one specific “tail” that waves its tail and one exceptional case that behaves differently.

0
source

Source: https://habr.com/ru/post/1265311/


All Articles