이번에는 Design Pattern의 Strategy Pattern을 프론트엔드에서 어떻게 사용하는지, 그리고 Design Pattern의 Strategy Pattern을 프론트엔드에서 사용할 때 주의사항에 대해 알려드리겠습니다. 실제 사례를 살펴보겠습니다.
4명의 GoF 형제가 쓴 고전적인 "디자인 패턴"에서 전략 패턴은 다음과 같이 정의됩니다.
일련의 알고리즘을 정의하고 이를 하나씩 캡슐화하고 상호 교환 가능하게 만듭니다.
위 문장은 말 그대로 매우 간단합니다. 하지만 이를 개발 과정에 어떻게 적용할지 여전히 하나의 정의만으로는 혼란스럽습니다. 저자가 한때 작업했던 상인 구매, 판매 및 재고 시스템을 예로 들어 보겠습니다.
한 슈퍼마켓에서 프로모션을 준비하고 있습니다. 조사 및 분석 후 시장 직원은 몇 가지 프로모션 전략을 세웠습니다. 100개 이상 구매
200개 이상 구매 시 30 할인
50 할인
300개 이상 구매 시. . .
캐셔 소프트웨어의 인터페이스는 다음과 같습니다(간단한 다이어그램).
초기 구현은 다음과 같습니다.
//方便起见,我们把各个促销策略定义为枚举值: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
판단으로 가득 차 있으므로 주의하지 않으면 실수하기 쉽습니다. .
좋아요, 일부 사람들은 제가 게으르다고 말합니다. 비록 이것이 충분히 우아하지는 않지만 결국에는 전체 축소 전략을 아무리 많이 사용해도 충분하지 않습니다.
요구사항은 결코 프로그래머에 의해 결정되지 않는다고 말씀드릴 수 있습니다. . 현재 마켓 직원은 새로운 버전의 프로그램에 멤버십 기능이 추가되었으며 다음과 같은 프로모션 전략을 지원해야 한다고 말했습니다. 그리고 첫 주문 10% 할인
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
판단을 함수에 추가해 주시면 될 것 같습니다. 당신의 코드를 보면 그는 애초에 왜 당신을 고용했는지 궁금해할 것입니다. . 🎜🎜자, 마침내 프로모션 전략의 코드를 리팩토링하기로 결정했습니다. 다음과 같이 할 수 있습니다. 🎜//方便起见,我们把各个促销策略定义为枚举值: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..
문도 수정해야 합니다🎜🎜🎜🎜전략의 정의를 검토해 보겠습니다. 패턴: 🎜🎜일련의 알고리즘을 정의합니다. 이는 하나씩 캡슐화되어 상호 교환 가능합니다. 이 예에서는 각 프로모션 전략의 구현이 다르지만 최종 목표는 실제 금액을 찾는 것입니다. 전략 패턴은 판촉 전략에 대한 알고리즘을 하나씩 캡슐화하고 실제 금액 평가에 영향을 주지 않으면서 상호 교환이 가능하도록 만들 수 있습니다. 이것이 바로 우리에게 필요한 것입니다. 🎜🎜 전략 패턴을 사용하여 위 코드를 재구성해 보겠습니다. 🎜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 다이어그램으로 표현하면 다음과 같습니다.
어때요? 이제 위의 사진을 보셨으니 확실히 이해가 되셨나요? 그렇다면 서둘러서 전략 모드를 플레이해 보세요!
참고 도서:
"디자인 패턴: 재사용 가능객체 지향소프트웨어의 기초"
"Dahua 디자인 패턴"
"J avascript 디자인 패턴 및 개발 연습》
저는 수년간 프론트엔드 개발을 해왔지만 디자인 패턴을 깊이있게 연구하고 정리한 적이 없습니다. 건축 관련 작업을 하다 보니 디자인 패턴이 제가 앞으로 나아가는 데 걸림돌이 된다는 걸 점점 더 많이 느껴요. 그러니 오늘부터 고전적인 디자인 패턴과 몇 가지 주요 객체지향 원칙을 깊이 연구하고 요약해 보세요.
오늘 첫날에는 먼저 전략 모드에 대해 이야기해보겠습니다.
4명의 GoF 형제가 쓴 고전적인 "디자인 패턴"에서 전략 패턴은 다음과 같이 정의됩니다.
일련의 알고리즘을 정의하고 이를 하나씩 캡슐화하고 상호 교환 가능하게 만듭니다.
위 문장은 말 그대로 매우 간단합니다. 하지만 이를 개발 과정에 어떻게 적용할지 여전히 하나의 정의만으로는 혼란스럽습니다. 저자가 한때 작업했던 상인 구매, 판매 및 재고 시스템을 예로 들어 보겠습니다.
한 슈퍼마켓에서 프로모션을 준비하고 있습니다. 조사 및 분석 후 시장 직원은 몇 가지 프로모션 전략을 세웠습니다. 100개 이상 구매
200개 이상 구매 시 30 할인
50 할인
300개 이상 구매 시. . .
캐셔 소프트웨어의 인터페이스는 다음과 같습니다(간단한 다이어그램).
초기 구현은 다음과 같습니다.
//方便起见,我们把各个促销策略定义为枚举值: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
판단으로 가득 차 있으므로 주의하지 않으면 실수하기 쉽습니다. .
좋아요, 일부 사람들은 제가 게으르다고 말합니다. 비록 이것이 충분히 우아하지는 않지만 결국에는 전체 축소 전략을 아무리 많이 사용해도 충분하지 않습니다.
요구사항은 결코 프로그래머에 의해 결정되지 않는다고 말씀드릴 수 있습니다. . 현재 마켓 직원은 새로운 버전의 프로그램에 멤버십 기능이 추가되었으며 다음과 같은 프로모션 전략을 지원해야 한다고 말했습니다. 그리고 첫 주문 10% 할인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
판단을 함수에 추가해 주시면 될 것 같습니다. 당신의 코드를 보면 그는 애초에 왜 당신을 고용했는지 궁금해할 것입니다. . 🎜🎜자, 마침내 프로모션 전략의 코드를 리팩토링하기로 결정했습니다. 다음과 같이 할 수 있습니다. 🎜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 중국어 웹사이트의 기타 관련 기사를 참조하세요!