Java设计模式:策略模式

 一、什么是策略模式

策略模式是一种行为型模式,它将对象和行为分开,将行为定义为一个行为接口和具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个 if 判断都可以理解为就是一个策略。本模式使得算法可独立于使用它的用户而变化。UML结构图如下:

其中的三种角色为:

  • 环境/上下文 (Context):持有一个策略类的引用,最终给客户端调用。
  • 抽象策略 (Strategy): 策略类,通常是一个接口或者抽象类。
  • 具体策略 (ConcreteStrategy):实现了策略类中的策略方法,封装相关的算法和行为。

二、策略模式的使用场景

1、多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。

2、需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。

3、对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

三、策略模式的代码示例

例如:超市中不同类型的商品,打折力度不同,在计算商品价格时,可能会这样写:

1、创建商品枚举类

public enum CommondityTypeEnum {

    COOKED_FOOD(0,"熟食"),
    FRUIT(1,"水果"),
    WINE(2,"酒水"),
    SNACKS(3,"零食");

    private Integer code;

    private String msg;

    CommondityTypeEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

2、方法中通过判断商品类型,根据折扣的不同,计算每种商品的价格

public BigDecimal calculation(BigDecimal price, int commondityType) {
        /**
         * 熟食区
         */
        if (commondityType == CommondityTypeEnum.COOKED_FOOD.getCode()) {
            //晚上八点之后打五折
            if (LocalDateTime.now().getHour() >= 20) {
                return price.multiply(new BigDecimal(0.5));
            }
        }
       /**
         * 水果区
         */
        if (commondityType == CommondityTypeEnum.FRUIT.getCode()) {
            //晚上八点之后打六折
            if (LocalDateTime.now().getHour() >= 20) {
                return price.multiply(new BigDecimal(0.6));
            }
        }
        /**
         * 酒水区
         */
        if (commondityType == CommondityTypeEnum.WINE.getCode()) {
            //不打折
            return price;
        }
        /**
         * 零食区
         */
        if (commondityType == CommondityTypeEnum.SNACKS.getCode()) {
            //周二会员日,均一价10元
            if ((Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1) == 2) {                
                return BigDecimal.valueOf(10);
            }
        }
        return price;
    }
}

这种多 if 判断的逻辑,很适合用策略模式改造:

在策略模式中,定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,在这里,每一个封装算法的类都可以称之为策略,为了保证这些策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应于一个具体策略类。

1、抽象策略类:

抽象出一个公共接口

public interface SuperMarket {
    // 计算价格
    BigDecimal calculation(BigDecimal price);

}

2、具体策略类:

/**
 * 具体策略类1:熟食区
 */
public class CookedFoodStrategy  implements SuperMarket{
    @Override
    public BigDecimal calculation(BigDecimal price) {
        //晚上八点之后打五折
        if (LocalDateTime.now().getHour() >= 20) {
            return price.multiply(new BigDecimal(0.5));
        }
        return price;
    }
}
/**
 * 具体策略2:水果区
 */
public class FruitStrategy implements SuperMarket {
    @Override
    public BigDecimal calculation(BigDecimal price) {
        //晚上八点之后打六折
        if (LocalDateTime.now().getHour() >= 20) {
            return price.multiply(new BigDecimal(0.6));
        }
        return price;
    }
}

酒水区、零食区等略过,如果后面有了其他商品类型,都很方便扩展;

3、上下文类:

/**
 * 上下文类:相当于收银台
 */
public class CashierContext {

    private SuperMarket superMarket;

    public CashierContext(SuperMarket superMarket) {
        this.superMarket = superMarket;
    }

    public BigDecimal checkOut(BigDecimal price) {
        return this.superMarket.calculation(price);
    }
}

4、测试类

public class TestMain {    
    public static void main(String[] args) {
        CashierContext cashierContext = new CashierContext(new CookedFoodStrategy());        
        System.out.println(cashierContext.checkOut(new BigDecimal(100)));
    }
}

四、策略模式的优缺点

优点:

  • 提供了管理相关的算法族的办法,从而避免重复的代码。
  • 提供了可以替换继承关系的办法。因为继承使得动态改变算法或行为变得不可能。
  • 避免使用多重条件转移语句。

缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很多。

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值