Detailed explanation of JavaScript policy Mode Programming, detailed explanation of javascript Policy
I like policy design patterns. I try to use it as much as possible. In essence, policy models use delegated decoupling to use their algorithm classes.
There are several advantages to doing so. It can prevent the use of large condition statements to determine which algorithms are used for specific types of objects. Separating concerns reduces the complexity of the client and promotes subclass composition. It improves modularity and testability. Each algorithm can be tested independently. Every client can simulate an algorithm. Any client can use any algorithm. They can communicate with each other. Just like Lego.
To implement the rule mode, there are usually two participants:
The policy object encapsulates the algorithm.
The client (context) object can use any policy in the out-of-the-box manner.
This section describes how to use the rule mode in Javascrip, how to use it to split a library into a small plug-in a chaotic and unordered environment, and plug-and-play packages.
Function as a policy
A function provides an excellent way to encapsulate algorithms and can be used as a policy. You only need to use a function on the client and make sure that your client can call this policy.
Let's use an example to prove it. Suppose we want to create a Greeter class. All it has to do is greet people. We hope that the Greeter class will know the different ways to greet people. To achieve this idea, we create different policies for greeting.
// 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.
In the preceding example, Greeter is a client and has three policies. As you can see, Greeter knows how to use algorithms, but has no idea about the details of algorithms.
For complex algorithms, a simple function is often not satisfied. In this case, the good way is to define according to the object.
Class as a policy
The policy can also be a class, especially when the calculation is more complex than the human (Policy/algorithm) used in the preceding example. If you use a class, you can define an interface for each policy.
In the following example, we confirm this.
// 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.'
By using classes, we define a policy with the anexecutemethod object. The client can use any policy to implement this interface.
Also, pay attention to how I create GreetingStrategy. The interesting part is the overload of methodexecute. It is defined in the form of other functions. The successor subclass of the current class can change the specific behavior, such as thesayHiorsayByemethod, without changing the conventional algorithm. This mode is called the template method and is very suitable for the policy mode.
Let's take a look.
// 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.'
GreetingStrategy creates a class algorithm by specifying theexecutemethod. In the code snippet above, we used this by creating a dedicated algorithm.
Without subclasses, our Greeter still shows a kind of polymorphism. There is no need to switch between different types of Greeter to trigger the correct algorithm. All of this is bound to every 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();});
Policy modes in multiple Environments
One of my favorite examples of policy patterns is actually in the Passport. js library. Passport. js provides a simple way to process authentication in Node. A wide range of vendors support (Facebook, Twitter, Google, etc.), and each is implemented as a strategy.
This library is feasible as an npm package, and all its policies are the same. Database users can decide which npm package to install for their specific use cases. The following code snippet shows how it is implemented:
// 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 Strategypassport.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); }); }));
The Passport. js library is equipped with only one or two simple authentication mechanisms. In addition, it does not exceed the interface of a policy class that conforms to the context object. This mechanism makes it easy for his users to implement their own authentication mechanism without adverse impact on the project.
Reflection
The rule mode provides a way to add modularity and testability to your code. This does not mean that (Policy mode) is always valid. Mixins can also be used for functional injection, such as an object algorithm at runtime. The flat old-fashioned duck-typing polymorphism can sometimes be simple enough.
However, the policy mode allows you to expand your code as the load type grows without introducing a large system at the beginning. As we can see in the Passport. js example, it will be more convenient for maintenance personnel to add additional policies in the future.