Couple to abstractions and not to concretions. It's one of these classic object oriented principles. But how do we actually follow it? In the book Elegant Objects by Yegor Bugayenko He has a chapter called Always Use Interfaces. In the chapter he discusses the importance of following the principle of always coupling to abstractions because it improves maintainability, which means we can change the code. Here's what he writes. Make sure that all public methods in your class implement some interface. So he's arguing that if you have a public method in your class that isn't an implementation of some method in the interface above it, then that method, that public method, is encouraging other classes to couple to this concrete class instead of coupling to an abstraction. It's tempting others to couple to concretions, when in fact we should be coupling to abstractions. If instead there was an interface above the class then other classes could couple to that interface instead. Less temptation. Such a simple but great idea, right? What do you think? Let's look at what this means. In a previous video, I argued that you should only use subtypes when you have variations in behavior and not when you only have variations in data. Because if you use subtypes, when you only have variations in data, then you make the code less flexible, meaning less maintainable, meaning less changeable. One of the examples that we looked at was this example. In the top you have an interface called IAttack with a method called Use that takes some argument of type IPlayer that we call the target. So you can imagine this is part of a turn based game where there is a fight, between different players and the different players use different attacks to attack each other. Then we have two subtypes one called Thunderbolt and one called Scratch. These would be classes that implement the interface. However, the issue that we discussed in the other video do check that for reference the issue that we discussed in that video was that the only difference between these two subtypes is that the implementation of Use either reduces the health of the target with 50 or with 25. So the only variation between these two subtypes is in terms of data. There is no behavioral variation. There is no algorithmic variation. So this is not a sensible use of subtypes. So the difference should be captured as objects instantiated with different parameters instead of subtypes with different implementations. So instead of these two subtypes we would end up with a single class called Attack. And then depending on how you construct objects of this type, you might end up with an object that corresponds to what was previously the subtype Thunderbolt or the subtype Scratch. But now these would be objects of type Attack. And by doing this we solve the problem of using subtypes for data variation. Because now we use classes for data variation because we don't have any behavioral variation here we only have data variation. So that's good. But what does this have to do with the discussion of coupling to abstractions rather than concretions? Well, what we did lose when doing this is that we lost our abstraction, because here IAttack was the abstraction? And what's good about that is that it encourages coupling to abstraction. Because there is an abstraction here, then users of attacks can coupled to this abstraction instead of the actual concretions. But now, now that we've removed this abstraction we are violating the rule of always using interfaces as per the book Elegant Objects. Because now we only have a concretion. So now we are encouraging others to couple to this particular class, which is not good. So when I'm saying that subtypes are not a good solution for capturing variations in data, I don't mean that we should couple to concretions. We should still couple to abstractions. I only mean that if all you have is data variation, you should not capture that data variation as subtypes. But that doesn't mean that your class can't have an abstraction above it, an interface above it. And if you follow the Always Use Interfaces rule of Elegant Objects, then it should. Which is why in that other video we introduced this other abstraction on a higher level of abstraction that we called IMove. where one of the subtypes is the class called Attack. But is it the only one? Probably not, because we can very easily imagine a bunch of other moves that you could use in this game. So over here in this solution, we get the best of both worlds. Wow. I forgot to fill this in. There you go. So by
choosing this strategy, we get the best of both worlds. We use classes for data variation. We don't use subtypes for data variation because all of the attacks are encapsulated in this idea of an attack of the concrete class Attack. But we are still encouraging coupling to abstractions because we provide an abstraction above our class. We don't have any other subtypes at the moment but we can clearly see it's somewhat obvious what another subtype would look like. And if it's somewhat obvious what another subtype would look like I don't spend any time worrying about Premature Abstraction. I just say introducing this interface is basically free and then I will couple to this interface instead and now I am prepared for introducing additional concretions later. But of course, if it isn't obvious what another subtype would look like, then we should probably stop, pause, and think about what the abstraction actually is. So we talk back and forth about tempting another class to couple to these concretions Instead of coupling to the abstractions. What might this other class be? What are we talking about? Just imagine something like again, A turn based battle between two players, then you might have something like a battle menu. And this battle menu might present you with a bunch of moves that you can make. And then it should be obvious that not all moves have to be attacks. Some of the moves could be other things, like for example, performing some kind of magic or whatever it is that your game contains. But nevertheless, if the battle menu couples to the concretion Attack then it is coupled to attack. And when we realize that we want more things than just attacks, then we have to change the battle menu. But if instead if instead of coupling to the concretion Attack we couple to an abstraction to the interface IMove and then happen to only use attacks then we are prepared for this inevitability when we discover that of course we want to have more things than just attacks. Because the other subtypes that we can imagine are somewhat obvious. And then we reap the benefits. So the battle menu would couple to an abstraction and not to a concretion. So hopefully you can see now how these two ideas are not at all in conflict with each other. The only thing I was saying in regards to the subtypes is that you should not use subtypes for capturing data variation. But that doesn't mean that we have to couple to concretions. We could still couple to abstractions and should couple to abstractions. So in summary we should avoid making subtypes that only vary in data because that makes the code less flexible. And instead we should use classes to capture variations in data. So that different objects can contain different data. But we should of course not encourage tempt others to couple to concretions. So we should indeed use classes and not subtypes to capture variations in data. But we should also make sure that all public methods in our concrete classes are implementations of a method in an interface above it. So that we encourage others to couple to abstractions and not to concretions. And so these two ideas are not in conflict but can be followed at the same time. I highly recommend the book Elegant Objects. It's a pretty new book, and some would say that it's a must read for modern object oriented developers. And I would say that the people who say that are what you might call right. Link to the book is in the description. Subscribe.