今回はデザインパターンの戦略パターンをフロントエンドで使用する方法と、デザインパターンの戦略パターンをフロントエンドで使用する際の注意点についてお届けします。実際のケースを見てみましょう。
GoF 4 兄弟による古典的な「デザイン パターン」では、戦略パターンは次のように定義されています。
一連のアルゴリズムを定義し、それらを 1 つずつカプセル化して、互換性を持たせる。
上記の文は文字通り非常に単純です。しかし、開発プロセスにそれをどのように適用するかは、定義が 1 つだけではまだわかりにくいです。著者がかつて取り組んだ小売業者の購買、販売、在庫システムを例に挙げます。
あるスーパーマーケットでは、調査と分析の後、いくつかのプロモーション戦略を策定しました。
は 10 割引です。 100以上の購入
200以上の購入で30割引
300以上の購入で50割引
。 。 。
レジソフトのインターフェースはこんな感じです(簡単な図):
実際の消費額はどのように計算すればよいでしょうか?
最初の実装は次のとおりです:
//方便起见,我们把各个促销策略定义为枚举值:0,1,2... var getActualTotal = function(onSaleType,originTotal){ if(onSaleType===0){ return originTotal-Math.floor(originTotal/100)*10 } if(onSaleType===1){ return originTotal-Math.floor(originTotal/200)*30 } if(onSaleType===0){ return originTotal-Math.floor(originTotal/300)*50 } } getActualTotal(1,2680); //2208
上記のコードは非常に単純ですが、その欠点も明らかです。私の完全削減戦略が徐々に増加するにつれて、getActualTotal
関数はますます大きくなり、if
の判断が満載になります。注意しないと間違いを犯しやすくなります。 。 getActualTotal
函数会越变越大,而且充满了if
判断,稍一疏忽就容易弄错。
OK,有人说我很懒,虽然这样不够优雅但并不影响我的使用,毕竟满减策略再多也多不到哪去。
我只能说,需求永远不是程序员定的。。这时,市场人员说我们新版程序添加了会员功能,我们需要支持以下的促销策略:
会员促销策略:
会员充300返60,且首单打9折
会员充500返100,且首单打8折
会员充1000返300,且首单打7折
...
这个时候,如果你还在原先的getActualTotal
函数中继续添加if
判断,我想如果你的领导review你这段代码,可能会怀疑自己当初怎么把你招进来。。
OK,我们终于下定决心要重构促销策略的代码,我们可以这么做:
var vipPolicy_0=function(originTotal){ return originTotal-Math.floor(originTotal/100)*10 } var vipPolicy_1=function(originTotal){ return originTotal-Math.floor(originTotal/200)*30 } ... //会员充1000返300 var vipPolicy_10=function(account,originTotal){ if(account===0){ account+=1300; return originTotal*0.9 }else{ account+=1300; return originTotal; } return originTotal-Math.floor(originTotal/200)*30 } ... var vipPolicy_n=function(){ ... } var getActualTotal=function(onSaleType,originTotal,account){ switch(onSaleType){ case 0: return vipPolicy_0(originTotal); case 1: return vipPolicy_0(originTotal); ... case n: return ... default: return originTotal; } }
好了,现在我们每种策略都有自己独立的空间了,看起来井井有条。但是还有两个问题没有解决:
随着促销策略的增加,getActualTotal
的代码量依然会越来越大
系统缺乏弹性,如果需要增加一种策略,那么除了添加一个策略函数,还需要修改switch...case..
语句
让我们再来回顾一下策略模式的定义:
定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换
在我们的例子中,每种促销策略的实现方式是不一样的,但我们最终的目的都是为了求得实际金额。策略模式可以把我们对促销策略的算法一个个封装起来,并且使它们可互相替换而不影响我们对实际金额的求值,这正好是我们所需要的。
下面我们用策略模式来重构上面的代码:
var policies={ "Type_0":function(originTotal){ return originTotal-Math.floor(originTotal/100)*10 }, "Type_1":function(originTotal){ return originTotal-Math.floor(originTotal/200)*30 }, ... "Type_n":function(originTotal){ ... } } var getActualTotal=function(onSaleType,originTotal,account){ return policies["Type_"+onSaleType](originTotal,account) } //执行 getActualTotal(0,2680.00);//2208
分析上面的代码我们发现,不管促销策略如何增加,getActualTotal
函数完全不需要再变化了。我们要做的,就是增加新策略的函数而已。
通过策略模式的代码,我们消除了让人反胃的大片条件分支语句,getActualTotal
getActualTotal
を使用している場合は、引き続き if
判定を関数に追加してください。リーダーがレビューしてくれると思います。コード、彼はそもそもなぜあなたを雇ったのか疑問に思うかもしれません。 。 🎜🎜OK、ついにプロモーション戦略のコードをリファクタリングすることにしました。これを行うことができます: 🎜//方便起见,我们把各个促销策略定义为枚举值:0,1,2... var getActualTotal = function(onSaleType,originTotal){ if(onSaleType===0){ return originTotal-Math.floor(originTotal/100)*10 } if(onSaleType===1){ return originTotal-Math.floor(originTotal/200)*30 } if(onSaleType===0){ return originTotal-Math.floor(originTotal/300)*50 } } getActualTotal(1,2680); //2208
getActualTotal
のコード量は依然として増大するでしょう 🎜🎜🎜🎜 システムは柔軟性に欠けています。戦略を追加する必要がある場合は、戦略関数の追加に加えて、switch...case..
ステートメントも変更する必要があります🎜🎜🎜🎜戦略の定義を確認してみましょうパターン: 🎜🎜一連のアルゴリズムを定義します。これらは 1 つずつカプセル化され、交換可能になります。この例では、各プロモーション戦略の実装は異なりますが、最終的な目標は実際の量を見つけることです。戦略パターンは、プロモーション戦略のアルゴリズムを 1 つずつカプセル化し、実際の金額の評価に影響を与えることなく互換性を持たせることができます。これはまさに私たちが必要としているものです。 🎜🎜 戦略パターンを使用して上記のコードを再構築しましょう: 🎜var vipPolicy_0=function(originTotal){ return originTotal-Math.floor(originTotal/100)*10 } var vipPolicy_1=function(originTotal){ return originTotal-Math.floor(originTotal/200)*30 } ... //会员充1000返300 var vipPolicy_10=function(account,originTotal){ if(account===0){ account+=1300; return originTotal*0.9 }else{ account+=1300; return originTotal; } return originTotal-Math.floor(originTotal/200)*30 } ... var vipPolicy_n=function(){ ... } var getActualTotal=function(onSaleType,originTotal,account){ switch(onSaleType){ case 0: return vipPolicy_0(originTotal); case 1: return vipPolicy_0(originTotal); ... case n: return ... default: return originTotal; } }
getActualTotal
関数はまったく変更する必要がないことがわかりました。 。新しい戦略の機能を追加するだけです。 🎜🎜 ストラテジ パターン コードを通じて、胃が痛むような条件分岐ステートメントを排除します。getActualTotal
自体には計算能力はありませんが、計算をストラテジ関数に委任します。 🎜🎜これから、戦略パターン実装の重要なポイントを要約できます: 🎜🎜🎜🎜変化するアルゴリズムを独立した戦略関数にカプセル化し、特定の計算を担当します🎜顧客のリクエストを受け付け、そのリクエストを特定の戦略関数に委任する
UML図で表すと以下のようになります。上の写真を見て、はっきりと理解できましたか?それなら急いでストラテジーモードに挑戦してください!
私は数年フロントエンド開発をしていますが、デザインパターンを深く勉強してまとめたことがありませんでした。建築関連の仕事をすることが多くなるにつれ、デザインパターンが自分の進むべき道において障害になっていると感じることが増えてきました。したがって、今日から古典的なデザイン パターンといくつかの主要なオブジェクト指向原則を深く研究し、要約することから始めましょう。
初日の今日は、まずストラテジーモードについて話しましょう。実際の消費額はどのように計算すればよいでしょうか?
最初の実装は次のとおりです:
//方便起见,我们把各个促销策略定义为枚举值:0,1,2... var getActualTotal = function(onSaleType,originTotal){ if(onSaleType===0){ return originTotal-Math.floor(originTotal/100)*10 } if(onSaleType===1){ return originTotal-Math.floor(originTotal/200)*30 } if(onSaleType===0){ return originTotal-Math.floor(originTotal/300)*50 } } getActualTotal(1,2680); //2208
getActualTotal
関数はますます大きくなり、if
の判断が満載になります。注意しないと間違いを犯しやすくなります。 。 OK、これは十分に洗練されていないと言う人もいますが、結局のところ、完全な削減戦略をどれだけ使用しても十分ではありません。 要件は決してプログラマによって決定されるものではないとしか言えません。 。現時点では、市場スタッフは、プログラムの新しいバージョンにはメンバーシップ機能が追加されており、次のプロモーション戦略をサポートする必要があると述べました:
メンバープロモーション戦略: getActualTotal
函数会越变越大,而且充满了if
判断,稍一疏忽就容易弄错。
OK,有人说我很懒,虽然这样不够优雅但并不影响我的使用,毕竟满减策略再多也多不到哪去。
我只能说,需求永远不是程序员定的。。这时,市场人员说我们新版程序添加了会员功能,我们需要支持以下的促销策略:
会员促销策略:
会员充300返60,且首单打9折
会员充500返100,且首单打8折
会员充1000返300,且首单打7折
...
这个时候,如果你还在原先的getActualTotal
函数中继续添加if
判断,我想如果你的领导review你这段代码,可能会怀疑自己当初怎么把你招进来。。
OK,我们终于下定决心要重构促销策略的代码,我们可以这么做:
var vipPolicy_0=function(originTotal){ return originTotal-Math.floor(originTotal/100)*10 } var vipPolicy_1=function(originTotal){ return originTotal-Math.floor(originTotal/200)*30 } ... //会员充1000返300 var vipPolicy_10=function(account,originTotal){ if(account===0){ account+=1300; return originTotal*0.9 }else{ account+=1300; return originTotal; } return originTotal-Math.floor(originTotal/200)*30 } ... var vipPolicy_n=function(){ ... } var getActualTotal=function(onSaleType,originTotal,account){ switch(onSaleType){ case 0: return vipPolicy_0(originTotal); case 1: return vipPolicy_0(originTotal); ... case n: return ... default: return originTotal; } }
好了,现在我们每种策略都有自己独立的空间了,看起来井井有条。但是还有两个问题没有解决:
随着促销策略的增加,getActualTotal
的代码量依然会越来越大
系统缺乏弹性,如果需要增加一种策略,那么除了添加一个策略函数,还需要修改switch...case..
getActualTotal
を使用している場合は、引き続き if
判定を関数に追加してください。リーダーがレビューしてくれると思います。コード、彼はそもそもなぜあなたを雇ったのか疑問に思うかもしれません。 。 🎜🎜OK、ついにプロモーション戦略のコードをリファクタリングすることにしました。これを行うことができます: 🎜var policies={ "Type_0":function(originTotal){ return originTotal-Math.floor(originTotal/100)*10 }, "Type_1":function(originTotal){ return originTotal-Math.floor(originTotal/200)*30 }, ... "Type_n":function(originTotal){ ... } } var getActualTotal=function(onSaleType,originTotal,account){ return policies["Type_"+onSaleType](originTotal,account) } //执行 getActualTotal(0,2680.00);//2208
getActualTotal
のコード量は依然として増大するでしょう 🎜🎜🎜🎜 システムは柔軟性に欠けています。ストラテジーを追加する必要がある場合は、ストラテジー関数の追加に加えて、switch...case..
ステートメントも変更する必要があります🎜🎜🎜🎜ストラテジー パターンの定義を確認してみましょうもう一度:🎜定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换
在我们的例子中,每种促销策略的实现方式是不一样的,但我们最终的目的都是为了求得实际金额。策略模式可以把我们对促销策略的算法一个个封装起来,并且使它们可互相替换而不影响我们对实际金额的求值,这正好是我们所需要的。
下面我们用策略模式来重构上面的代码:
var policies={ "Type_0":function(originTotal){ return originTotal-Math.floor(originTotal/100)*10 }, "Type_1":function(originTotal){ return originTotal-Math.floor(originTotal/200)*30 }, ... "Type_n":function(originTotal){ ... } } var getActualTotal=function(onSaleType,originTotal,account){ return policies["Type_"+onSaleType](originTotal,account) } //执行 getActualTotal(0,2680.00);//2208
分析上面的代码我们发现,不管促销策略如何增加,getActualTotal
函数完全不需要再变化了。我们要做的,就是增加新策略的函数而已。
通过策略模式的代码,我们消除了让人反胃的大片条件分支语句,getActualTotal
本身并没有计算能力,而是将计算全权委托给了策略函数。
由此我们可以总结出策略模式实现的要点:
将变化的算法封装成独立的策略函数,并负责具体的计算
委托函数,该函数接受客户请求,并将请求委托给某一个具体的策略函数
用一张UML图表示如下:
怎么样?现在看到上面这张图是不是有了了然于胸的感觉?那就赶紧去试一试策略模式吧!
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がデザインパターンの戦略パターンをフロントエンドで使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。