三、行为型模式【策略模式】

策略模式

定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用算法的代码

工厂模式是解耦对象的创建和使用。
观察者模式是解耦观察者和被观察者。
策略模式解耦的是策略的定义、创建、使用,控制代码的复杂度,让每个部分都不至于过于复杂、代码量过多。

  • 策略的定义
    包含一个策略接口和一组实现这个接口的策略类

  • 策略的创建
    策略模式会包含一组策略,在使用它们的时候,一般会通过类型(type)来判断创建哪个策略来使用。可以把根据 type 创建策略的逻辑抽离出来,放到工厂类中。


    如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可以被共享使用的,不需要在每次都创建一个新的策略对象。

    public class StrategyFactory {
        private static final Map<String, Strategy> strategies = new HashMap<>();
    
        static {
            strategies.put("A", new ConcreteStrategyA());
            strategies.put("B", new ConcreteStrategyB());
        }
    
        public static Strategy getStrategy(String type) {
            if (type == null || type.isEmpty()) {
                throw new IllegalArgumentException("type should not be empty.");
            }
            return strategies.get(type);
        }
    }
    
    

    如果策略类是有状态的,根据业务场景的需要,我们希望每次从工厂方法中,获得的都是新创建的策略对象

    public class StrategyFactory {
        public static Strategy getStrategy(String type) {
            if (type == null || type.isEmpty()) {
                throw new IllegalArgumentException("type should not be empty.");
            }
            if (type.equals("A")) {
                return new ConcreteStrategyA();
            } else if (type.equals("B")) {
                return new ConcreteStrategyB();
            }
            return null;
        }
    }
    
    
  • 策略的使用
    运行时动态确定,根据配置文件的配置决定使用哪种策略

利用策略模式避免分支判断

public class OrderService {
    public double discount(Order order) {
        double discount = 0.0;
        OrderType type = order.getType();
        if (type.equals(OrderType.NORMAL)) { // 普通订单
//...省略折扣计算算法代码
        } else if (type.equals(OrderType.GROUPON)) { // 团购订单
//...省略折扣计算算法代码
        } else if (type.equals(OrderType.PROMOTION)) { // 促销订单
            //...省略折扣计算算法代码
        }
        return discount;
    }
}
// 策略的定义
public interface DiscountStrategy {
    double calDiscount(Order order);
}

// 省略NormalDiscountStrategy、GrouponDiscountStrategy、PromotionDiscountStrategy

// 策略的创建
public class DiscountStrategyFactory {
    private static final Map<OrderType, DiscountStrategy> strategies = new HashMa

    static {
        strategies.put(OrderType.NORMAL, new NormalDiscountStrategy());
        strategies.put(OrderType.GROUPON, new GrouponDiscountStrategy());
        strategies.put(OrderType.PROMOTION, new PromotionDiscountStrategy());
    }

    public static DiscountStrategy getDiscountStrategy(OrderType type) {
        return strategies.get(type);
    }
}

// 策略的使用
public class OrderService {
    public double discount(Order order) {
        OrderType type = order.getType();
        DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStra
        return discountStrategy.calDiscount(order);
    }
}

重构之后的代码就没有了 if-else 分支判断语句了。这得益于策略工厂类。在工厂类中,用 Map 来缓存策略,根据 type 直接从 Map 中获取对应的策略,从而避免 if-else 分支判断逻辑。

如果业务场景需要每次都创建不同的策略对象,就要用另外一种工厂类的实现方式

public class DiscountStrategyFactory {
    public static DiscountStrategy getDiscountStrategy(OrderType type) {
        if (type == null) {
            throw new IllegalArgumentException("Type should not be null.");
        }
        if (type.equals(OrderType.NORMAL)) {
            return new NormalDiscountStrategy();
        } else if (type.equals(OrderType.GROUPON)) {
            return new GrouponDiscountStrategy();
        } else if (type.equals(OrderType.PROMOTION)) {
            return new PromotionDiscountStrategy();
        }
        return null;
    }
}

这种实现方式相当于把原来的 if-else 分支逻辑,从 OrderService 类中转移到了工厂类中,实际上并没有真正将它移除。(基于查表法来解决)

可以通过反射来避免对策略工厂类的修改。具体是这么做的:通过一个配置文件或者自定义的 annotation 来标注都有哪些策略类;策略工厂类读取配置文件或者搜索被 annotation 标注的策略类,然后通过反射了动态地加载这些策略类、创建策略对象;当我们新添加一个策略的时候,只需要将这个新添加的策略类添加到配置文件或者用 annotation 标注即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值