Branch Statement Optimization — Strategy Pattern
Published: 2019-01-25
In many business scenarios, there are often multiple ways to complete a task. For example, many of our products have different promotion methods, such as spend 100 save 10, spend 200 return 40, buy three get one free, etc. Each method we call astrategy . Each strategy is essentially a different algorithm for handling the same business problem. Each algorithm is independent and can replace another, so the strategy pattern is:
Define a family of algorithms, encapsulate each one, and make them interchangeable. The strategy pattern lets the algorithm vary independently from the clients that use it. The strategy pattern is a behavioral object pattern.
Learn more
In project development, many requirements are implemented using different algorithms for the same need, such as searching, sorting, and form validation. I’ll use form validation, which is more common on the frontend, as an example. A common approach is to hard-code validation methods in a shared utility class. If there are multiple validation behaviors, you can put the corresponding validation algorithms in the utility class, exposing one method per algorithm for use. Below is how we wrote validation in our train ticket and hotel H5 projects: we placed validation methods at the same level as other utility methods in the tools class.
Of course, you can also encapsulate these validation algorithms in a single method and select among them with if…else… or other conditional statements. Both implementations can be calledhard-coded; if you need to add a new validation method, you must modify the source code of the utility class that contains the algorithms; changing the search algorithm also requires modifying the caller code.
When using some animation libraries, you will often see code like the following:
animate({width: '200px'}, 1000, 'linear')Here, the third parameter can choose different easing algorithms, such as linear or easeout. The complex algorithmic processing is hidden from the caller—this is a classic use of the strategy pattern.
Pattern example
We’d like form validation calls to be just as elegant. Below is a form regex validation strategy object.
// Form regex validation strategy object
var Inputstrategy = function() {
var strategy = {
// whether empty
notNull: function(value) {
return /\s+/.test(value) ? 'Please enter content' : ''
},
// whether it’s a number
number: function(value) {
return /^[0-9]+(\.[0-9]+)?$/.test(value) ? '' : 'Please enter a number'
},
// whether it’s a local phone number
phone: function(value) {
return /^\d{3}\-\d{8}$|^\d{4}\-\d{7}$/.test(value) ? '' : 'Please enter a valid phone number'
}
}
return {
// validate type algorithm value form value
check: function (type, value) {
// trim leading and trailing whitespace
value = value.replace(/^\s+|\s+$/g, '');
return strategy[type] ? strategy[type](value) : 'No detection method for this type'
},
// add strategy
addStrategy: function (type, fn) {
strategy[type] = fn
}
}
}()As you can see, besides exporting a check method for validation, this strategy class also exports an addStrategy method to add strategies. This allows temporary modification of strategy methods without changing the internal code of the strategy object.
Inputstrategy.ckeck('nickname', 'xxxxxxxx')This calling style is clearer and more readable, and the algorithm implementation classes are no longer bloated and disorganized.
Summary
The main advantage of the strategy patternis its excellent support for the “open–closed principle”, allowing you to replace or add algorithms without modifying the existing system. It manages families of algorithms well, improves code reuse, serves as an alternative to inheritance, and avoids complex conditional branching. Its drawback is that the client must know all strategy classes and understand their differences, which increases the cost of using strategy classes. It also increases the number of classes in the system; there may be many strategy classes.
There are three patterns related to optimizing branching statements: factory method, state, and strategy patterns. For the factory method pattern, it is a creational pattern whoseultimate goal is object creation . Both the state pattern and the strategy pattern are behavioral patterns. In the state pattern, its core iscontrolling state to determine different behavioral outcomes, so states are usually not interchangeable; otherwise they would produce different behaviors. By contrast,the core of the strategy pattern is the algorithm, and each algorithm typically handles the same business logic, so they can be interchangeable. Strategy classes don’t need to care about the caller’s environment, because the result produced by a given strategy is fixed.
References
JavaScript Design Patterns — Zhang Rongming
Last updated