策略模式简介
定义
定义一系列的算法,把每一个算法封装起来,并且使它们可以相互替换。策略模式能使算法与用户端而独立存在与变化。
例子
某商场为迎接国庆的到来,决定开启商品促销活动,活动细节如下
针对不同会员等级优惠的折扣有所不同,同时与消费金额所关联,消费金额在不同范围折扣优惠率也不一样。
- 普通会员:没有任何优惠折扣
- 初级会员:消费100-200元有5%的促销折扣,消费200(含200)以上元有10%促销折扣
- 中级会员:消费100-200元有10%的促销折扣,消费200(含200)以上元有15%促销折扣
- 高级会员:消费100-200元有15%的促销折扣,消费200(含200)以上元有20%促销折扣
最后需计算促销折扣后,实付金额。
为了实现上面的需求,通常我们会这样写,如下:
public class StragetyTest {
public static void main(String[] args) {
StragetyTest test = new StragetyTest();
test.getPayAmount("初级会员", 150);
test.getPayAmount("中级会员", 150);
test.getPayAmount("高级会员", 150);
}
public double getPayAmount(String memberType, double amount) {
double payAmout=0;
switch (memberType) {
case "初级会员":
if (amount>=100 && amount<200 ) {
//促销折扣5%
payAmout=amount*0.95;
}else if (amount>=200) {
//促销折扣10%
payAmout=amount*0.9;
}else {
//没有优惠折扣
payAmout=amount;
}
break;
case "中级会员":
if (amount>=100 && amount<200 ) {
//促销折扣10%
payAmout=amount*0.9;
}else if (amount>=200) {
//促销折扣15%
payAmout=amount*0.85;
}else {
//没有优惠折扣
payAmout=amount;
}
break;
case "高级会员":
if (amount>=100 && amount<200 ) {
//促销折扣15%
payAmout=amount*0.85;
}else if (amount>=200) {
//促销折扣20%
payAmout=amount*0.8;
}else {
//没有优惠折扣
payAmout=amount;
}
break;
default:
//没有优惠折扣
payAmout=amount;
break;
}
System.out.println("原价:"+amount+"元,"+memberType+"优惠后的实际价格:"+payAmout+"元");
return payAmout;
}
}
上面的代码,需求倒是实现了,如果活动促销规则临时有变,需要对普通会员消费有促销力度……此时就在增加对普通会员优惠金额的计算了,在原代码需增加if-else判断语句了,如果每个会员等级的商品促销折扣算法变的更复杂,各种判断语言交错其中,那么代码就会变的特别浮肿,后期维护扩展困难,增加开发的成本。这可违背了设计模式中的单一职责原则。
下面通过策略模式,进行重构。如下:
1、定义一个用于计算促销折扣后的实际价格的接口(策略接口)
public interface MemberStrategy {
double getPayAmount(double amount);
}
2、分别定义三个会员等级的具体产品类,它们共同的特点实现了计算促销优惠后的价格(具体策略实现)
//初级会员
public class PrimaryMemberStrategy implements MemberStrategy{
@Override
public double getPayAmount(double amount) {
double payAmout;
if (amount>=100 && amount<200 ) {
//促销折扣5%
payAmout=amount*0.95;
}else if (amount>=200) {
//促销折扣10%
payAmout=amount*0.9;
}else {
//没有优惠折扣
payAmout=amount;
}
System.out.println("原价:"+amount+"元,初级会员优惠后的实际价格:"+payAmout+"元");
return payAmout;
}
}
//中级会员
public class MiddleMemberStrategy implements MemberStrategy{
@Override
public double getPayAmount(double amount) {
double payAmout;
if (amount>=100 && amount<200 ) {
//促销折扣10%
payAmout=amount*0.9;
}else if (amount>=200) {
//促销折扣15%
payAmout=amount*0.85;
}else {
//没有优惠折扣
payAmout=amount;
}
System.out.println("原价:"+amount+"元,中级会员优惠后的实际价格:"+payAmout+"元");
return payAmout;
}
}
//高级会员
public class AdvancedMemberStrategy implements MemberStrategy{
@Override
public double getPayAmount(double amount) {
double payAmout;
if (amount>=100 && amount<200 ) {
//促销折扣15%
payAmout=amount*0.85;
}else if (amount>=200) {
//促销折扣20%
payAmout=amount*0.8;
}else {
//没有优惠折扣
payAmout=amount;
}
System.out.println("原价:"+amount+"元,高级会员优惠后的实际价格:"+payAmout+"元");
return payAmout;
}
}
3、定义一个控制器,包含了每个会员等级,可以传递不同具体会员对象来调用需要的方法。
public class MemberContext {
private MemberStrategy mStrategy;
//使用多态来接受对象值。
public void setMemberStrategy(MemberStrategy mStrategy) {
this.mStrategy = mStrategy;
}
public double getPayAmount(double amount){
return mStrategy.getPayAmount(amount);
}
}
4、用户使用
public class StragetyTest {
public static void main(String[] args) {
MemberContext memberContext = new MemberContext();
//初级会员
memberContext.setMemberStrategy(new PrimaryMemberStrategy());
memberContext.getPayAmount(150);
//中级会员
memberContext.setMemberStrategy(new MiddleMemberStrategy());
memberContext.getPayAmount(150);
//高级会员
memberContext.setMemberStrategy(new AdvancedMemberStrategy());
memberContext.getPayAmount(150);
}
}
最后的结果:
原价:150.0元,初级会员优惠后的实际价格:142.5元
原价:150.0元,中级会员优惠后的实际价格:135.0元
原价:150.0元,高级会员优惠后的实际价格:127.5元
可以看出,使用策略模式,把每个会员等级促销算法独立出来,互不影响,也避免了对会员等级switch-case判断语句,降低了代码的耦合性,有利于维护,即使增加对普通会员的促销策略,也无需改动原来的代码,只需新增策略具体类即可,不仅体现了设计模式中职责单一原则,还体现了开放封闭原则,
同时缺点也会明显,如果策略过多,会增加过多的策略类,最后导致类的数量剧增。
针对上面的策略模式的例子,用UML类图体现如下:
UML结构图
角色介绍:
- MemberStrategy:抽象策略角色,算法方法的抽象,通常是接口
- Strategy:具体策略类,实现了抽象策略接口,算法的具体实现。
- MemberContext:上下文角色,用来操控每个策略类的上下文环境。屏蔽了高层模块对具体算法的访问,提现了设计模式中的依赖倒置原则。
使用场景
在开发中,凡是有同类型类似的算法需求,可以尝试使用策略模式进行封装来应对不同的变化。