策略模式
一、策略模式简介
抽象会员接口
public interface VIPCard { double sale(double money); }
普通会员不打折
public class CommonVip implements VIPCard { @Override public double sale(double money) { return 1 * money; } }
黄金会员打九折
public class GoldVip implements VIPCard{ @Override public double sale(double money) { return 0.9 * money; } }
钻石会员打八折
public class DiamondVip implements VIPCard { @Override public double sale(double money) { return 0.8 * money; } }
具体环境,超市打折计算金额
public class Shop{ private VIPCard vipCard; public Shop(VIPCard vipCard){ this.vipCard=vipCard; } public double sale(double money){ return vipCard.sale(money); } }
测试代码
public class Test { public static void main(String[] args) { double price=100; //一共消费一百元 VIPCard vipCard=new CommonVip(); //普通会员 Shop shop=new Shop(vipCard); //普通会员打折策略 double salePrice=shop.sale(price); System.out.println("会员卡打折后价格:"+salePrice); vipCard=new DiamondVip(); //钻石会员 shop=new Shop(vipCard); //钻石会员打折策略 salePrice=shop.sale(price); System.out.println("会员卡打折后价格:"+salePrice); } }
运行结果
这里在超市结账的时候,动态的根据不同的会员,使用不同的打折策略来计算实付金额。
二、深入理解策略模式
通过上面的例子,你可能知道了如何去实现一个策略模式,但还是迷惑为什么使用它,什么时候使用它,以及使用它的好处。
上面的例子中,不同的会员会有不同的折扣,我们知道会员卡在消费的时候都有增加积分的功能。那么现在它们都有一个增加积分的功能,却有各自不同的打折策略,我们第一想到的就是使用继承的方式来达到代码的复用以及子类重写父类方法来实现自己的打折策略,代码如下:
抽象会员父类,实现计算积分方法,可变的打折策略,让子类去实现。
public abstract class VIPCard { public int bonusPoints(double money){ return (int)money; } public abstract double sale(double money); }
普通会员继承自会员父类,同时拥有计算积分方法,重写自己的打折策略
public class CommonVip extends VIPCard { @Override public double sale(double money) { return 1* money; } }
黄金会员继承自会员父类,同时拥有计算积分方法,重写自己的打折策略
public class GoldVip extends VIPCard{ @Override public double sale(double money) { return 0.9 * money; } }
钻石会员继承自会员父类,同时拥有计算积分方法,重写自己的打折策略
public class DiamondVip extends VIPCard { @Override public double sale(double money) { return 0.8 * money; } }
超市结账时对金额打折计算
public class Shop{ private VIPCard vipCard; public Shop(VIPCard vipCard){ this.vipCard=vipCard; } public double sale(double money){ return vipCard.sale(money); } }
这么一看,确实完美,即达到了代码的复用性,不同会员又有了自己不同的打折策略。这时候,超市为了提高消费者的购买激情,推出活动,黄金会员以上,消费满100元送洗衣粉一袋。这时候,在父类中新加一个送洗衣粉的方法
public abstract class VIPCard { public int bonusPoints(double money){ return (int)money; } public String sendWashingPowder(double money){ return money>100?"消费满一百,送您一袋洗衣粉":"您的消费不满一百元,无法领取该礼品"; } public abstract double sale(double money); }
注意:在父类中新增方法,所有子类都会继承该方法。普通会员和白银会员是没有资格参与该活动的,所以它们不应该拥有送洗衣粉的方法。当然,可以在白银会员和普通会员子类中重写送洗衣粉的方法,在方法中保留空白内容即可,但这种方法是不可取的。每当新增可变的功能的时候,都会影响到其它的子类,需要去修改这些子类,牵一发而动全身,可见这种设计是非常不好的。
有人可能想到,把变化的部分抽离出来做成接口,为需要该功能的类实现接口就可以了。代码如下
抽象送礼品接口
public interface SendGift { String sendGift(double money); }
黄金会员符合送礼品条件,实现接口
public class GoldVip extends VIPCard implements SendGift{ @Override public double sale(double money) { return 0.9 * money; } @Override public String sendGift(double price) { return money>=100?"消费满一百,送您一袋洗衣粉":"您的消费不满一百元,无法领取该礼品"; } }
钻石会员符合送礼品条件,实现接口
public class DiamondVip extends VIPCard implements SendGift{ @Override public double sale(double money) { return 0.8 * money; } @Override public String sendGift(double money) { return money>=100?"消费满一百,送您一袋洗衣粉":"您的消费不满一百元,无法领取该礼品"; } }
这样看起来也蛮符合需求的,满足送礼品等级的会员就实现送礼品的行为。仔细观察,发现这两个类中都实现了相同的代码,没有达到代码复用的作用,这样代码就显得冗余了,这也不是一个很好的改造方式。
那么我们用策略模式来改造代码,看效果如何。
先将可变的部分抽离出来组成策略族
public interface SendGift { String sendGift(double money); }
public class SendWashingPowder implements SendGift{ @Override public String sendGift(double money) { return money>=100?"消费满一百,送您一袋洗衣粉":"您的消费不满一百元,无法领取该礼品"; } }
public class NoSendWashingPowder implements SendGift { @Override public String sendGift(double money) { return "您不符合会员等级条件,暂无法参与该活动"; } }
会员类修改如下
public abstract class VIPCard { protected SendGift sendGift; public int bonusPoints(double money){ return (int)money; } public String sendGift(double money){ return sendGift.sendGift(money); } public abstract double sale(double money); }
普通会员不送礼品
public class CommonVip extends VIPCard { public CommonVip(){ this.sendGift=new NoSendWashingPowder(); } @Override public double sale(double money) { return 1* money; } }
钻石会员送洗衣粉
public class DiamondVip extends VIPCard{ public DiamondVip(){ this.sendGift=new SendWashingPowder(); } @Override public double sale(double money) { return 0.8 * money; } }
黄金会员送洗衣粉
public class GoldVip extends VIPCard{ public GoldVip(){ this.sendGift=new SendWashingPowder(); } @Override public double sale(double money) { return 0.9 * money; } }
超市类调用
public class Shop{ private VIPCard vipCard; public Shop(VIPCard vipCard){ this.vipCard=vipCard; } public double sale(double money){ return vipCard.sale(money); } public String SendGift(double money){ return vipCard.sendGift(money); } }
测试代码
public static void main(String[] args) { double money=100; //一共消费一百元 VIPCard vipCard=new CommonVip(); //普通会员 Shop shop=new Shop(vipCard); //普通会员打折策略 double salePrice=shop.sale(money); System.out.println("您此次消费金额为:"+money+" 会员卡打折后价格:"+salePrice); System.out.println(shop.SendGift(money)); //普通会员去领取礼物 vipCard=new DiamondVip(); //钻石会员 shop=new Shop(vipCard); //钻石会员打折策略 salePrice=shop.sale(money); System.out.println("您此次消费金额为:"+money+" 会员卡打折后价格:"+salePrice); System.out.println(shop.SendGift(money)); //钻石会员去领取礼物 } }
执行结果
可以看到,使用策略模式很好的解决了上面的代码复用性的问题。
总结:策略模式提供了可以替换继承关系的办法。策略模式是将可变的行为抽象成一个策略族,在程序运行的时候动态传递。策略模式适合使用在行为经常变化的场景中。继承是一种强依赖的关系,继承使得行为与策略紧密绑定在了一起,无法割舍。而策略模式遵循了设计原则中的多用组合,少用继承。