Let's begin our introduction to subtype polymorphism with a problem. Imagine a feature that checks if there are comments on an article or topic (questions from Hexlet users under tutorials and projects). The article in the code is represented by an object with the class Article and the topic is Topic.
const hasComments = (commentable) => {
// If this is an article
if (commentable instanceof Article) {
return commentable.getArticleComments().length > 0;
// If this is a topic
} else if (commentable instanceof Topic) {
return commentable.getTopicComments().length > 0;
}
}
class Article {
// some code
getArticleComments() {
return this.comments;
}
}
class Topic
{
// some code
getTopicsComments()
{
return this.comments;
}
}
// Article.first() is the method that returns the first article from the database
const article = Article.first();
console.log(hasComments(article));
We've encountered this kind of code before. Each new type will require us to add code to this conditional construct. It can be replaced by dispatching by key, but it won't make it much better. You still have to define the behavior for each class in addition to how the behavior has already been defined within each class. But there's something even better you can do. It's enough to agree on the interface for all types and agree that the method for retrieving comments will be called getComments()
. Then the code will be like this:
const hasComments = (commentable) => commentable.getComments().length > 0;
const article = Article.first();
console.log(hasComments(article));
const topic = Topic.first();
console.log(hasComments(topic));
The hasComments(commentable)
function can now be called with any object that has a getComments()
method with the necessary signature. This function won't change even if you add a new class containing the same method.
The ability of a function to handle objects of different types in the same way is called subtype polymorphism, and the function itself is called a polymorphic function.
As you can see from the code above, you don't need inheritance and interfaces in JavaScript to implement this kind of polymorphism. This approach is often called “duck typing". If something walks like a duck and quacks like a duck, it is a duck.
Technically, the simplest and most straightforward thing that subtype polymorphism does (for client code) is to remove conditional constructs. Any conditional construct can be replaced by polymorphism, and any polymorphic function can be replaced by an if
. In other words, subtype polymorphism is not an integral part of development; code can be written without it. On the other hand, sometimes there are situations in which it helps a lot, but it doesn't happen all the time.
What's the difference between parametric polymorphism and subtype polymorphism? In the first case, a general algorithm is implemented for a container (such as an array) that contains a T type value or values. This algorithm is independent of T and is executed identically for any T value. In the second case, the algorithm is built around the object itself and uses its methods. In subtype polymorphism, the polymorphic function works only with objects that have the necessary methods to implement the algorithm.
The Hexlet support team or other students will answer you.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.
Programming courses for beginners and experienced developers. Start training for free
Our graduates work in companies:
From a novice to a developer. Get a job or your money back!
Sign up or sign in
Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.