Home > Web Front-end > JS Tutorial > Detailed explanation of JavaScript strategy pattern programming_Basic knowledge

Detailed explanation of JavaScript strategy pattern programming_Basic knowledge

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
Release: 2016-05-16 15:53:13
Original
1064 people have browsed it

I like the strategy design pattern. I try to use it as much as possible. At its core, the Strategy pattern uses delegates to decouple the algorithm classes that use them.

There are several benefits to doing this. It prevents the use of large conditional statements to decide which algorithms to use for specific types of objects. Separating concerns thus reduces client complexity while also facilitating subclassing. It improves modularity and testability. Each algorithm can be tested individually. Every client can simulate algorithms. Any client can use any algorithm. They can intermodulate. Just like Legos.

To implement the Strategy Pattern, there are usually two participants:

The object of this strategy encapsulates the algorithm.

Client (context) object that can use any strategy in a plug-and-play manner.

Here is an introduction to how I use the strategy pattern in Javascript and how to use it in a chaotic environment to split the library into small plug-ins and plug-and-play packages.

Functions as strategies

A function provides an excellent way to encapsulate an algorithm and can be used as a strategy. Just pass a function to the client and make sure your client can call the policy.

Let’s use an example to prove. Suppose we want to create a Greeter class. All it has to do is say hello to people. We want the Greeter class to know the different ways to greet people. To implement this idea, we create different strategies for greetings.

// Greeter is a class of object that can greet people.
// It can learn different ways of greeting people through
// 'Strategies.'
//
// This is the Greeter constructor.
var Greeter = function(strategy) {
this.strategy = strategy;
};
 
// Greeter provides a greet function that is going to
// greet people using the Strategy passed to the constructor.
Greeter.prototype.greet = function() {
return this.strategy();
};
 
// Since a function encapsulates an algorithm, it makes a perfect
// candidate for a Strategy.
//
// Here are a couple of Strategies to use with our Greeter.
var politeGreetingStrategy = function() {
console.log("Hello.");
};
 
var friendlyGreetingStrategy = function() {
console.log("Hey!");
};
 
var boredGreetingStrategy = function() {
console.log("sup.");
};
 
// Let's use these strategies!
var politeGreeter = new Greeter(politeGreetingStrategy);
var friendlyGreeter = new Greeter(friendlyGreetingStrategy);
var boredGreeter = new Greeter(boredGreetingStrategy);
 
console.log(politeGreeter.greet()); //=> Hello.
console.log(friendlyGreeter.greet()); //=> Hey!
console.log(boredGreeter.greet()); //=> sup.
Copy after login

In the above example, Greeter is the client and has three strategies. As you can see, Greeter knows how to use the algorithm, but has no idea about the details of the algorithm.

For complex algorithms, a simple function often cannot satisfy. In this case, the best approach is to define it in terms of objects.

Classes as Strategies

Strategies can also be classes, especially when the calculation is more complex than the artificial (policy/algorithm) used in the above example. Using classes allows you to define an interface for each strategy.

In the example below, this is confirmed.

// We can also leverage the power of Prototypes in Javascript to create
// classes that act as strategies.
//
// Here, we create an abstract class that will serve as the interface
// for all our strategies. It isn't needed, but it's good for documenting
// purposes.
var Strategy = function() {};
 
Strategy.prototype.execute = function() {
 throw new Error('Strategy#execute needs to be overridden.')
};
 
// Like above, we want to create Greeting strategies. Let's subclass
// our Strategy class to define them. Notice that the parent class
// requires its children to override the execute method.
var GreetingStrategy = function() {};
GreetingStrategy.prototype = Object.create(Strategy.prototype);
 
// Here is the `execute` method, which is part of the public interface of
// our Strategy-based objects. Notice how I implemented this method in term of
// of other methods. This pattern is called a Template Method, and you'll see
// the benefits later on.
GreetingStrategy.prototype.execute = function() {
 return this.sayHi() + this.sayBye();
};
 
GreetingStrategy.prototype.sayHi = function() {
 return "Hello, ";
};
 
GreetingStrategy.prototype.sayBye = function() {
 return "Goodbye.";
};
 
// We can already try out our Strategy. It requires a little tweak in the
// Greeter class before, though.
Greeter.prototype.greet = function() {
 return this.strategy.execute();
};
 
var greeter = new Greeter(new GreetingStrategy());
greeter.greet() //=> 'Hello, Goodbye.'
Copy after login

By using a class, we define a strategy with anexecutemethod object. Clients can implement this interface using any strategy.

Also notice how I created the GreetingStrategy. The interesting part is the overloading of methodexecute. It is defined in the form of other functions. Now subsequent subclasses of the class can change specific behaviors, such as thesayHiorsayByemethod, without changing the general algorithm. This pattern is called the template method and is very suitable for the strategy pattern.

Let’s see what happens.

// Since the GreetingStrategy#execute method uses methods to define its algorithm,
// the Template Method pattern, we can subclass it and simply override one of those
// methods to alter the behavior without changing the algorithm.
 
var PoliteGreetingStrategy = function() {};
PoliteGreetingStrategy.prototype = Object.create(GreetingStrategy.prototype);
PoliteGreetingStrategy.prototype.sayHi = function() {
 return "Welcome sir, ";
};
 
var FriendlyGreetingStrategy = function() {};
FriendlyGreetingStrategy.prototype = Object.create(GreetingStrategy.prototype);
FriendlyGreetingStrategy.prototype.sayHi = function() {
 return "Hey, ";
};
 
var BoredGreetingStrategy = function() {};
BoredGreetingStrategy.prototype = Object.create(GreetingStrategy.prototype);
BoredGreetingStrategy.prototype.sayHi = function() {
 return "sup, ";
};
 
var politeGreeter  = new Greeter(new PoliteGreetingStrategy());
var friendlyGreeter = new Greeter(new FriendlyGreetingStrategy());
var boredGreeter  = new Greeter(new BoredGreetingStrategy());
 
politeGreeter.greet();  //=> 'Welcome sir, Goodbye.'
friendlyGreeter.greet(); //=> 'Hey, Goodbye.'
boredGreeter.greet();  //=> 'sup, Goodbye.'
Copy after login

GreetingStrategy creates a class algorithm by specifying the steps of theexecutemethod. In the code snippet above, we take advantage of this by creating a specialized algorithm.

Without subclassing, our Greeter still exhibits polymorphic behavior. There is no need to switch between different types of Greeter to trigger the correct algorithm. This is all bound to each Greeter object.

var greeters = [
 new Greeter(new BoredGreetingStrategy()),
 new Greeter(new PoliteGreetingStrategy()),
 new Greeter(new FriendlyGreetingStrategy()),
];
 
greeters.forEach(function(greeter) {
  
 // Since each greeter knows its strategy, there's no need
 // to do any type checking. We just greet, and the object
 // knows how to handle it.
 greeter.greet();
});
Copy after login

Strategy mode in multiple environments

One of my favorite examples of the Strategy Pattern is found in the Passport.js library. Passport.js provides a simple way to handle authentication in Node. A wide range of providers support it (Facebook, Twitter, Google, etc.), each implemented as a policy.

The library is available as an npm package, as are all its strategies. Users of the library can decide which npm package to install for their unique use cases. Here is a code snippet showing how this is done:

// Taken from http://passportjs.org
 
var passport = require('passport')
   
  // Each authentication mechanism is provided as an npm package.
  // These packages expose a Strategy object.
 , LocalStrategy = require('passport-local').Strategy
 , FacebookStrategy = require('passport-facebook').Strategy;
 
// Passport can be instanciated using any Strategy.
passport.use(new LocalStrategy(
 function(username, password, done) {
  User.findOne({ username: username }, function (err, user) {
   if (err) { return done(err); }
   if (!user) {
    return done(null, false, { message: 'Incorrect username.' });
   }
   if (!user.validPassword(password)) {
    return done(null, false, { message: 'Incorrect password.' });
   }
   return done(null, user);
  });
 }
));
 
// In this case, we instanciate a Facebook Strategy
passport.use(new FacebookStrategy({
  clientID: FACEBOOK_APP_ID,
  clientSecret: FACEBOOK_APP_SECRET,
  callbackURL: "http://www.example.com/auth/facebook/callback"
 },
 function(accessToken, refreshToken, profile, done) {
  User.findOrCreate(..., function(err, user) {
   if (err) { return done(err); }
   done(null, user);
  });
 }
));
Copy after login

The Passport.js library only comes with one or two simple authentication mechanisms. Other than that, it has no interface beyond a policy class that conforms to the context object. This mechanism allows its users to easily implement their own authentication mechanisms without adversely affecting the project.

Reflection

The Strategy pattern provides a way to increase modularity and testability to your code. This doesn't mean (Strategy Pattern) always works. Mixins can also be used to inject functionality, such as algorithms into an object at runtime. Flat old duck-typing polymorphism can sometimes be simple enough.

However, using the Strategy pattern allows you to scale your code as your workload grows without introducing a large architecture at the beginning. As we saw with the Passport.js example, it will become easier for maintainers to add additional strategies in the future.

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Issues
What are JavaScript hook functions?
From 1970-01-01 08:00:00
0
0
0
What is JavaScript garbage collection?
From 1970-01-01 08:00:00
0
0
0
c++ calls javascript
From 1970-01-01 08:00:00
0
0
0
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template