Register to get access to free programming courses with interactive exercises

Strategy (Pattern) JS: Polymorphism

Let's reinforce the theory we've learned with one practical example that shows a typical application of subtype polymorphism.

Imagine the task of calculating the cost of travel insurance. The cost of insurance depends on a large number of factors. Some factors can affect how companies calculate the price by changing the formula itself.

In the case of insurance, there is likely one formula where the values are substituted, and the calculation happens in one go. The main thing for us now is the concept itself, not precise knowledge of the inner workings of the insurance business.

If you solve this problem head-on, it will look like a big mess of calculations with many conditional constructs. Over time, such a code becomes extremely difficult to read because of the many states you keep in mind:

// A few examples we came up with
if (age < 18) {
  let cost = salary * age;

  if (country === 'uganda') {
    cost = cost / 2;
  }
} else if (age >= 18 && age < 24) {
    // ...
}

Is there any way to make the code clearer and easier to understand? Sometimes yes. Of all the factors involved in the calculation, you should try to find those that affect it globally. They manifest as a global if at the top level.

Let's assume that in the case of insurance, it's age. We believe that age determines the formula for calculating the cost of insurance.

The next step is to look at the branches in this conditional design and specify the ranges. Let's say it looks like this:

  • Under 18
  • 18 to 24
  • 24 to 65
  • Older than 65

There's something important you need to take note of. Above, we agreed that each age group determines the algorithm for calculating the insurance cost. They're independent of each other, although the calculation process itself may be similar in places (and most likely will be).

Now let's move from the logical structure to the code. Each age group is the class responsible for calculating the cost for that group:

class LessThan18 {
  // Parameters are the factors on which the calculation is based
  calculate(params) {
    // Here we count and return the result
  }
}

// The name is not that great. In real code you should think of something more meaningful
class MoreThan18AndLessThan24 {
  // The structure of the parameters must be 100% the same as the rest of the classes,
  // since only in this case is polymorphism possible
  calculate(params) {
      // Here we count and return the result
  }
}

// Other classes

The main thing we got was to divide the calculation process into independent blocks of code, which are easier to understand. Each such class is called a strategy (computation).

It's important for the strategy not to be an abstraction, an object with a state and lifetime. Therefore, we pass to the constructor not the data but the method itself.

In essence, it's an ordinary function packed into a class with only one purpose: to get subtype polymorphism. We can do this via the function dispatching by keys to simplify the code.

Next, the following question arises: how and where do we choose the implementation to work with? There are several options here.

We can delegate the choice of implementation to an external code. If we apply dependency inversion, that means we're working with a ready-made strategy:

calculateCost(strategy, params) {
  strategy.calculate(params);
}

So far, we've only gotten away from the problem; we haven't solved it. Either way, there will be code somewhere that contains either a conditional construct or implements one of the dispatching methods we covered in previous lessons. In the simplest case, this code would look like this:

chooseCostInsuranceStrategy(user) {
  if (user.getAge() < 18) {
    return new LessThan18();
  } else if (/* ... */) {
    // some code
  }
}

strategy = chooseCostInsuranceStrategy(user);
strategy.calculate(params);

As you can see from the examples above, there'll be more code using the strategy, but not as much as if you were to use the dispatching of functions by associative array keys.

It applies to virtually all situations where subtype polymorphism is involved in JavaScript. It is the price you have to pay for a division that simplifies code expansion and reduces its complexity.

On the other hand, it is easy to fall into the trap and, conversely, make the code even more complex than it was before the introduction of subtype polymorphism.

This polymorphism makes the code verbose and overly abstract if applied mindlessly. You don't need expansion as often as people say you do. Moreover, you can invert dependencies as you go along whenever you need to.


Recommended materials

  1. Expression Problem

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

For full access to the course you need a professional subscription.

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.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

Bookmate
Health Samurai
Dualboot
ABBYY
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

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.