策略模式
一、策略模式簡介
抽象會員接口
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)); //鑽石會員去領取禮物
}
}
執行結果
可以看到,使用策略模式很好的解決了上面的代碼複用性的問題。
總結:策略模式提供了可以替換繼承關係的辦法。策略模式是將可變的行爲抽象成一個策略族,在程序運行的時候動態傳遞。策略模式適合使用在行爲經常變化的場景中。繼承是一種強依賴的關係,繼承使得行爲與策略緊密綁定在了一起,無法割捨。而策略模式遵循了設計原則中的多用組合,少用繼承。