戦略パターンの定義
ストラテジパターンの定義とは、一連のアルゴリズムをカプセル化したストラテジオブジェクトを定義することであり、アルゴリズムを柔軟に切り替えて使用することができます。
これは主に、類似したアルゴリズムが複数ある場合に if と else を使用することによって引き起こされる複雑で保守が難しい問題を解決します。
戦略パターンの例
ある日、鍛冶屋aさんは刀「sa」を作り、自分の刀が鍛冶界の中で一番だと主張し、一日中仲間の前で誇らしげに披露していました。
その話は徐々に広がり、それを聞いた隣の街の鍛冶屋Bさんは不機嫌になり、大好きな作品であるソードSBを持って鍛冶屋Aさんと勝負することになりました。
両刀のデータは以下の通り。
const sword1 = {
name: 'sa',//宝剑的名字sa
sharpness: 999,//宝剑的锋利度,这数据确实是值得吹嘘
vulnerability: 100,//宝剑的脆弱度,可以说是比较脆弱了
magic: '101',//宝剑的魔法ID
}
const sword2 = {
name: 'sb',//宝剑的名字sb
sharpness: 500,//宝剑的锋利度,也是一把锋利的绝世好剑
vulnerability: 10,//宝剑的脆弱度,这把剑算得上比较坚硬
magic: '102',//宝剑的魔法ID
}
鍛冶屋Aはひげを撫でて微笑んで、切れ味比較器を取り出し、二人でその上に刀を置きました。
function compareSharpness (s1, s2) {
if (s1.sharpness > s2.sharpness) {
console.log(s1.name + '胜利')
} else {
console.log(s2.name + '胜利')
}
}
コンパレータが作動します
compareSharpness(sword1, sword2)
結果はsaさんの勝ちです。鍛冶屋 B はそれを見て、コンパレーターに何か問題があると言いました。どうやって剣の切れ味だけを比較できるのでしょうか? いいえ、脆弱性を追加する必要があります。そこで鍛冶屋 B はコンパレータを取り出してテーブルの上に叩きつけました。
function compareSharpnessAndDurability (s1, s2) {
const point = 0
for (let key in s1) {
if (key === 'sharpness') {
point += (s1[key] - s2[key])
} else if (key === 'vulnerability') {
point += ((s2[key] - s1[key]) * 10)
//脆弱度是越低越好的,而且脆弱评分的比例要比锋利度高
}
}
if (point > 0) {
console.log(s1.name + '胜利')
} else {
console.log(s2.name + '胜利')
}
}
このコンパレータは次のように開始します。
compareSharpnessAndDurability(sword1, sword2)
明らかに剣sbが勝ちました。しかし今度は鍛冶屋Aが不満を抱き、それは世界を滅ぼす大魔法を放つ魔剣だったと言う。鍛冶屋Bは反撃した、満足するな、私も大魔法、聖滅を放つことができる。そこで 2 人は共同で新しいコンパレータを購入しました。
//这个比较器比较先进,记录了一些魔法的评分
const magicList = {
'101': {
name: '毁天灭地',
point: 7000
},
'102': {
name: '神圣湮灭',
point: 6000
}
}
function compareSword (s1, s2) {
const point = 0
for (let key in s1) {
if (key === 'sharpness') {
point += (s1[key] - s2[key])
} else if (key === 'vulnerability') {
point += ((s2[key] - s1[key]) * 10)
//脆弱度是越低越好的,而且脆弱评分的比例要比锋利度高
} else if (key === 'magic') {
point += (magicList[s1[key]].point - magicList[s2[key]].point)
//魔法评分要读取魔法表
}
}
if (point > 0) {
console.log(s1.name + '胜利')
} else {
console.log(s2.name + '胜利')
}
}
今回もSAが勝ちました。鍛冶屋Bさんは、納得して諦めたそうですが、何かが違うと思いました。魔法がちょっと外れただけです。なぜこんなにスコアが違うのですか?と思い、静かに比較会社に相談しました。
Comparator 社は少し調査を行った結果、魔法と脆弱性のスコアの割合はこの方法で計算されるべきではなく、剣にはより多くのデータがあり、より複雑なロジックに変更する必要があると述べました。そこで、数十の if-else と数千の行を含むコンパレーターが誕生しました...その後の開発者は、開始できないと言いました。
では、戦略パターンを使用するとどうなるでしょうか?
比較戦略をオブジェクトにカプセル化し、オブジェクトの属性名に従って比較戦略を動的に選択します。
コードは次のようになります。
const strats = {
sharpness: function (v1, v2) {
return v1 - v2
},
vulnerability: function (v1, v2) {
return (v2 - v1) * 10
},
magic: function (v1, v2) {
return magicList[v1].point - magicList[v2].point
},
}
const magicList = {
'101': {
name: '毁天灭地',
point: 7000
},
'102': {
name: '神圣湮灭',
point: 6000
}
}
function compareSword (s1, s2) {
const point = 0
for (let key in s1) {
point += strats[key](s1[key], s2[key])
}
if (point > 0) {
console.log(s1.name + '胜利')
} else {
console.log(s2.name + '胜利')
}
}
今後、比較対象の属性を追加する場合も、属性の比較方法を変更する場合も、ストラテジ内で変更するだけでよく、異なるストラテジであっても、別ファイルに分けて記述することが可能です。
戦略パターンのメリットとデメリット
利点: 1. アルゴリズムを自由に切り替えることができます。2. 複数の条件判断を使用しないでください。3. 優れた拡張性。
デメリット: 1. 戦略機能が増える。
戦略パターンの利用シナリオ
- システムは、いくつかのアルゴリズムから 1 つを動的に選択する必要があります。
- オブジェクトには多くの動作があり、それらは複数の条件付き選択ステートメントを使用して実装されます。
戦略パターンに基づくフォームバリデーター
戦略パターンの理解に基づいて、戦略パターンに基づいてフォームバリデータを作成しました。
まずコンストラクター関数 Validator を定義します。
const Validator = function(errFunc){
this.cache=[]
this.errFunc=errFunc
}
キャッシュは、現在検証する必要があるすべての文字列を記録するために使用される検証機能です。
errFunc は、エラー情報を処理するために使用される関数です。
別のポリシー オブジェクトを定義します。
Validator.strategies = {
/**
* @param {string} errorMsg - 错误信息
*/
isNonEmpty: function (errorMsg) {
if(!errorMsg){
errorMsg="输入不能为空"
}
return function (value) {
if (value === "") {
return errorMsg;
}
};
},
/**
* @param {number} length - 最小长度
* @param {string} errorMsg - 错误信息
*/
minLength: function (length, errorMsg) {
if(!errorMsg){
errorMsg="输入内容过少"
}
return function (value) {
if (value.length < length) {
return errorMsg;
}
};
},
/**
* @param {number} length - 最大长度
* @param {string} errorMsg - 错误信息
*/
maxLength: function (length, errorMsg) {
if(!errorMsg){
errorMsg="输入内容不能超出长度限制"
}
return function (value) {
if (value.length > length) {
return errorMsg;
}
};
},
/**
* @param {string} errorMsg - 错误信息
*/
isMobile: function (errorMsg) {
if(!errorMsg){
errorMsg="手机号输入有误"
}
return function (value) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
};
},
};
この戦略では、開発で一般的に使用される 4 つのフォーム検証戦略 (NULL 判定、最大長と最小長の制限、携帯電話番号の検証など) が定義されています。各ストラテジの最後のパラメータはエラー メッセージです。次に、検証戦略の追加です。値には複数の検証戦略を含めることができます。
/**
*
* @param {string} value - 待验证字符串
* @param {function} strategies - 验证策略
*/
Validator.prototype.add=function(value,strategies){
if(strategies instanceof Array){
for(var i=0;i<strategies;i++){
this.cache.push(strategies[i].bind(null,value))
}
}else{
this.cache.push(strategies.bind(null,value))
}
}
パラメーター ストラテジは、定義した一連のストラテジの 1 つまたは複数で渡す必要があります。チェック メソッドがトリガーされるまで、すべての関数がキャッシュにプッシュされます。
/**
* 验证所有的值的合法性
*/
Validator.prototype.check=function(retain){
for(var i=0;i<this.cache.length;i++){
var msg=this.cache[i]()
if(msg){
this.errFunc(msg)
return false
}
}
if(!retain){
this.cache=[]
}
return true
}
check メソッドはキャッシュ内のすべての関数を実行し、不一致が見つかった場合は false を返します。
フォームバリデータの使用
コードだけを見ると、このフォーム バリデーターの使い方が少しわかりにくいかもしれませんが、フォーム バリデーターの使用方法の例をいくつか示します。
var text = "123456"
var validator = new Validator(console.log)//传入这个函数是用于提示错误信息的,可以传入$alert来提示错误信息
validator.add(text,Validator.strategies.minLength(4,'长度未达到要求!'))//加入最小长度为4的验证策略
validator.add(text,Validator.strategies.maxLength(6,'太长了!!!'))//加入最大长度为6
validator.check()
add メソッドの 2 番目のパラメーターに配列の形式で戦略を入力することもできます。
//同等效果
validator.add(text,[Validator.strategies.minLength(4),Validator.strategies.maxLength(6)])
//验证策略的错误信息有默认值,可不填
validator.check()
検証された文字列に問題がない場合、validator.check() は true を返します。検証された文字列が検証戦略の要件を満たしていない場合、validator.check() は false を返し、エラーを返してエラー プロンプト関数を呼び出します。情報を最初のパラメータとして指定すると、エラー メッセージが表示されます。