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. This is insurance that you're recommended to purchase before going abroad in case of sudden illness or injury.

The cost of the insurance depends on a large number of factors. Some of the factors can affect how the cost is calculated, not by changing the figures, but by changing the formula itself.

Specifically, in the case of insurance, it's likely that there is one big formula where the values are substituted, and the calculation is done 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 lots of conditional constructs. Over time, such a code becomes extremely difficult to read because of the large number of states you need to keep in your head.

// 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 the calculation globally. They manifest as a global if at the top level. Let's assume that in the case of insurance, it's age. I.e., 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 what ranges are specified there. 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 cost of insurance. I.e., 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 less than fab, of course, in real code you should think of something that means something
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 very important for the strategy to not be an abstraction, an object with a state and lifetime. Therefore, the data is not passed to the constructor, but to the method itself. In essence, it's an ordinary function (computation) that's packed into a class with only one purpose: to get subtype polymorphism. All this can be done via dispatching of functions by keys, and the code will be simpler.

Next, the following question arises: how and where do we choose the right implementation to work with? There are several options here. The choice of implementation can be delegated to external code, i.e., 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 dispatching of functions by associative array keys. This applies to virtually all situations in which subtype polymorphism is involved in JavaScript. This is the price you have to pay for a division that simplifies code expansion and reduces its complexity. On the other hand, it's very 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 left, right, and center. And 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

Hexlet Experts

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

The Hexlet support team or other students will answer you.

About Hexlet learning process

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:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs

From a novice to a developer. Get a job or your money back!

Frontend Developer icon
Profession
beginner
Development of front-end components for web applications
start anytime 10 months

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.