詳解JavaScript的策略模式編程
我喜歡策略設(shè)計(jì)模式。我盡可能多的試著去使用它。究其本質(zhì),策略模式使用委托去解耦使用它們的算法類。
這樣做有幾個(gè)好處。他可以防止使用大條件語句來決定哪些算法用于特定類型的對(duì)象。將關(guān)注點(diǎn)分離開來,因此降低了客戶端的復(fù)雜度,同時(shí)還可以促進(jìn)子類化的組成。它提高了模塊化和可測(cè)性。每一個(gè)算法都可以單獨(dú)測(cè)試。每一個(gè)客戶端都可以模擬算法。任意的客戶端都能使用任何算法。他們可以互調(diào)。就像樂高積木一樣。
為了實(shí)現(xiàn)策略模式,通常有兩個(gè)參與者:
該策略的對(duì)象,封裝了算法。
客戶端(上下文)對(duì)象,以即插即用的方式能使用任何策略。
這里介紹了我在Javascrip里,怎樣使用策略模式,在混亂無序的環(huán)境中怎樣使用它將庫拆成小插件,以及即插即用包的。
函數(shù)作為策略
一個(gè)函數(shù)提供了一種封裝算法的絕佳方式,同時(shí)可以作為一種策略來使用。只需通過一個(gè)到客戶端的函數(shù)并確保你的客戶端能調(diào)用該策略。
我們用一個(gè)例子來證明。假設(shè)我們想創(chuàng)建一個(gè)Greeter 類。它所要做的就是和人打招呼。我們希望Greeter 類能知道跟人打招呼的不同方式。為了實(shí)現(xiàn)這一想法,我們?yōu)榇蛘泻魟?chuàng)建不同的策略。
// 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.
在上面的例子中,Greeter 是客戶端,并有三種策略。正如你所看到的,Greeter 知道怎樣使用算法,但對(duì)于算法的細(xì)節(jié)卻一無所知。
對(duì)于復(fù)雜的算法,一個(gè)簡(jiǎn)單的函數(shù)往往不能滿足。在這種情況下,對(duì)好的方式就是按照對(duì)象來定義。
類作為策略
策略同樣可以是類,特別是當(dāng)算比上述例子中使用的人為的(策略/算法)更復(fù)雜的時(shí)候。使用類的話,允許你為每一種策略定義一個(gè)接口。
在下面的例子中,證實(shí)了這一點(diǎn)。
// 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.'
通過使用類,我們與anexecutemethod對(duì)象定義了一個(gè)策略??蛻舳丝梢允褂萌魏尾呗詫?shí)現(xiàn)該接口。
同樣注意我又是怎樣創(chuàng)建GreetingStrategy的。有趣的部分是對(duì)methodexecute的重載。它以其他函數(shù)的形式定義?,F(xiàn)在類的后繼子類可以改變特定的行為,如thesayHiorsayByemethod,并不改變常規(guī)的算法。這種模式叫做模板方法,非常適合策略模式。
讓我們看個(gè)究竟。
// 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 通過指定theexecutemethod的步驟,創(chuàng)建了一個(gè)類的算法。在上面的代碼片段中,我們通過創(chuàng)建專門的算法從而利用了這一點(diǎn)。
沒有使用子類,我們的Greeter 依然展示出一種多態(tài)行為。沒有必要在Greeter 的不同類型上進(jìn)行切換來觸發(fā)正確的算法。這一切都綁定到每一個(gè)Greeter 對(duì)象上。
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();
});
多環(huán)境下的策略模式
我最喜歡的有關(guān)策略模式的例子之一,實(shí)在 Passport.js庫中。Passport.js提供了一種在Node中處理身份驗(yàn)證的簡(jiǎn)單方式。大范圍內(nèi)的供應(yīng)商都支持(Facebook, Twitter, Google等等),每一個(gè)都作為一種策略實(shí)現(xiàn)。
該庫作為一個(gè)npm包是可行的,其所有的策略也一樣。庫的用戶可以決定為他們特有的用例安裝哪一個(gè)npm包。下面是展示其如何實(shí)現(xiàn)的代碼片段:
// 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);
});
}
));
Passport.js庫只配備了一兩個(gè)簡(jiǎn)單的身份驗(yàn)證機(jī)制。除此之外,它沒有超過一個(gè)符合上下文對(duì)象的一個(gè)策略類的接口。這種機(jī)制讓他的使用者,很容易的實(shí)現(xiàn)他們自己的身份驗(yàn)證機(jī)制,而對(duì)項(xiàng)目不產(chǎn)生不利的影響。
反思
策略模式為你的代碼提供了一種增加模塊化和可測(cè)性的方式。這并不意味著(策略模式)總是有效。Mixins 同樣可以被用來進(jìn)行功能性注入,如在運(yùn)行時(shí)的一個(gè)對(duì)象的算法。扁平的老式 duck-typing多態(tài)有時(shí)候也可以足夠簡(jiǎn)單。
然而,使用策略模式允許你在一開始沒有引入大型體系的情況下,隨著負(fù)載型的增長(zhǎng),擴(kuò)大你的代碼的規(guī)模。正如我們?cè)赑assport.js例子中看到的一樣,對(duì)于維護(hù)人員在將來增加另外的策略,將變得更加方便。
- 從表單校驗(yàn)看JavaScript策略模式的使用詳解
- JavaScript設(shè)計(jì)模式之策略模式實(shí)現(xiàn)原理詳解
- javascript設(shè)計(jì)模式 – 策略模式原理與用法實(shí)例分析
- JavaScript同源策略和跨域訪問實(shí)例詳解
- JavaScript設(shè)計(jì)模式之策略模式詳解
- javascript設(shè)計(jì)模式之策略模式學(xué)習(xí)筆記
- 利用策略模式與裝飾模式擴(kuò)展JavaScript表單驗(yàn)證功能
- 輕松掌握J(rèn)avaScript策略模式
- 學(xué)習(xí)JavaScript設(shè)計(jì)模式之策略模式
- javascript設(shè)計(jì)模式--策略模式之輸入驗(yàn)證
- 學(xué)習(xí)JavaScript設(shè)計(jì)模式(策略模式)
- 深入理解JavaScript系列(19):求值策略(Evaluation strategy)詳解
- 深入理解JavaScript系列(33):設(shè)計(jì)模式之策略模式詳解
- JavaScript設(shè)計(jì)模式之策略模式實(shí)例
- 怎樣用Javascript實(shí)現(xiàn)策略模式
相關(guān)文章
淺談JavaScript 標(biāo)準(zhǔn)對(duì)象
下面小編就為大家?guī)硪黄獪\談JavaScript 標(biāo)準(zhǔn)對(duì)象。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06
javascript cookie用法基礎(chǔ)教程(概念,設(shè)置,讀取及刪除)
這篇文章主要介紹了javascript cookie用法,結(jié)合實(shí)例形式總結(jié)分析了javascript中cookie的定義、特點(diǎn)及獲取、設(shè)置、刪除等基本操作技巧,需要的朋友可以參考下2016-09-09
javascript事件綁定學(xué)習(xí)要點(diǎn)
這篇文章主要介紹了javascript事件綁定學(xué)習(xí)要點(diǎn),主要包含下面四個(gè)方面1.傳統(tǒng)事件綁定的問題,2.W3C事件處理函數(shù),3.IE事件處理函數(shù),4.事件對(duì)象的其他補(bǔ)充,有需要的小伙伴可以參考下2016-03-03
javascript向flash swf文件傳遞參數(shù)值注意細(xì)節(jié)
如何使用javascript向SWF文件傳遞參數(shù)?在網(wǎng)上找了一個(gè)完整的教程,很有啟發(fā)性和實(shí)用性,如下是完整實(shí)現(xiàn)的步驟,需要的朋友可以參考下2012-12-12

