Please correct me of any of statements in this thread are wrong.
I read that reusing code is the primary benefit of inheritance, but it dawned on me that polymorphism through subtyping is the primary advantage of inheritance and makes it possible to reuse code in complex systems.
In many articles online, I see subtyping constantly combined with polymorphism. I understand that subtyping is a means to achieve overriding in inheritance patterns. Although, both are different forms of polymorphism.
Link in the below code as an example:
class Logger
{
public void count(string filepath) {
Writeline("I'm counting lines of %s pretty hard!\n", $filepath);
}
}
class JSONLogger extends Logger {
public override void count(string filepath) {
Writeline("I'm counting JSON objects from file %s pretty hard!\n", $filepath);
}
}
class Shipment
{
private Logger logger;
public Shipment (Logger logger) {
this.logger = logger;
}
public void count(string filepath) {
this.logger.count(filepath);
}
}
shipment = new Shipment(new JSONLogger());
shipment.count("/my/shipment");
Here, we use a subtype of Logger, that is JSON Logger, to override the count() method.
But was subtyping meant to be used with overriding? Isn't mixing overriding and subtyping bad?
For example, the user who wants to create an instance of Shipment will need to understand what Logger to us? A regular logger or a JSONLogger?
The user will need to look at how both these loggers are implemented to decide on which behavior he needs to use. But doesn't this violate the principle of abstraction?
We create objects and abstract behavior so we don't need to worry about how they are implemented. If the above code was a more complex inheritance tree with more classes and more overriding, then we need to know all this extra information about how sub-class override super class behavior and if the superclasses themselves overrode any base class behavior, etc.
In this case, isn't it actually hard to practically implement subtyping or substitute objects dynamically during run time?
So, is mixing overriding and subtyping bad? or is overriding in general just bad design?
EDIT: Reason I had these questions was because I read an article about the LSP principle by Barbara Liskov. She states that
Objects of subtypes should behave like those of supertypes if used via supertype methods
According to this statement, if we want to use polymorphism together with inheritance, we need to have proper subtyping by following this rule: when you substitute one superclass by its subclass, the behavior of the whole system should be unchanged.
Doesn't Overriding change the behavior since you have a new implementation of the superclass method in the subclass?
I think you are asking the wrong questions and using the wrong terminologies. Overriding actually refers to the act of re-implementing a superclass method in a subclass and it's actually quite common. There's no more inherent issue than with inheritance itself. However, if you are overriding all the methods without super calls then inheritance becomes pointless as there's no inherited behavior. In the above example,
Loggerwould most likely be an interface rather than a class and you'd have 2 concrete implementations of that interface.That's not an override. Delegating the implementation to an injected collaborator is referred to as the Strategy Pattern and it's one of the most widely used design pattern.
Often times, the client actually wants to be in control of the strategy. For instance, when you create a
TreeSetin java (sorted set), you may pass aComparatorto be used for custom sorting. However, there are also cases where you want to relieve the client from making such decisions explicitly and you might abstract away that part of the creation process using one of the creational patterns.As a side note, I understand your example is most likely not taken from the real world, but a problem with the design seems to be that
Shipmenthas too many responsibilities and works with collaborators at the wrong level of abstraction. AShipmentmost likely shouldn't have any knowledge of file paths, etc.