行为型-策略模式

策略模式是一种行为设计模式,用于封装一系列算法并使它们可以互换。文章通过股票排序的例子展示了如何使用策略模式,包括环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)的实现。还讨论了使用枚举和策略工厂来简化和增强策略模式的灵活性和可扩展性。
摘要由CSDN通过智能技术生成

策略模式简介

策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
策略模式主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。

  • 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
  • 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。
    uml示例图如下:
    在这里插入图片描述
    简单策略实现
    数据DTO
@Data
public class Stock {

    // 股票交易代码
    private String code;

    // 现价
    private Double price;

    // 涨幅
    private Double rise;
}

定义策略接口

public interface Strategy {

    /**
     * 将股票列表排序
     *
     * @param source 源数据
     * @return 排序后的榜单
     */
    List<Stock> sort(List<Stock> source);
}

策略实现类

/**
 * 高价榜
 */
public class HighPriceRank implements Strategy {

    @Override
    public List<Stock> sort(List<Stock> source) {
        return source.stream()
                .sorted(Comparator.comparing(Stock::getPrice).reversed())
                .collect(Collectors.toList());
    }
}

/**
 * 低价榜
 */
public class LowPriceRank implements Strategy {

    @Override
    public List<Stock> sort(List<Stock> source) {
        return source.stream()
                .sorted(Comparator.comparing(Stock::getPrice))
                .collect(Collectors.toList());
    }
}

/**
 * 高涨幅榜
 */
public class HighRiseRank implements Strategy {

    @Override
    public List<Stock> sort(List<Stock> source) {
        return source.stream()
                .sorted(Comparator.comparing(Stock::getRise).reversed())
                .collect(Collectors.toList());
    }
}

环境角色

public class Context {
    private Strategy strategy;
    
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public List<Stock> getRank(List<Stock> source) {
        return strategy.sort(source);
    }
}

调用类–榜单实例RankServiceImpl

@Service
public class RankServiceImpl {

    /**
     * dataService.getSource() 提供原始的股票数据
     */
    @Resource
    private DataService dataService;

    /**
     * 前端传入榜单类型, 返回排序完的榜单
     *
     * @param rankType 榜单类型
     * @return 榜单数据
     */
    public List<Stock> getRank(String rankType) {
        // 创建上下文
        Context context = new Context();
        // 这里选择策略
        switch (rankType) {
            case "HighPrice":
                context.setStrategy(new HighPriceRank());
                break;
            case "LowPrice":
                context.setStrategy(new LowPriceRank());
                break;
            case "HighRise":
                context.setStrategy(new HighRiseRank());
                break;
            default:
                throw new IllegalArgumentException("rankType not found");
        }
        // 然后执行策略
        return context.getRank(dataService.getSource());
    }
}

策略枚举

枚举策略类

public enum RankEnum {
    // 以下三个为策略实例
    HighPrice {
        @Override
        public List<Stock> sort(List<Stock> source) {
            return source.stream()
                    .sorted(Comparator.comparing(Stock::getPrice).reversed())
                    .collect(Collectors.toList());
        }
    },
    LowPrice {
        @Override
        public List<Stock> sort(List<Stock> source) {
            return source.stream()
                    .sorted(Comparator.comparing(Stock::getPrice))
                    .collect(Collectors.toList());
        }
    },
    HighRise {
        @Override
        public List<Stock> sort(List<Stock> source) {
            return source.stream()
                    .sorted(Comparator.comparing(Stock::getRise).reversed())
                    .collect(Collectors.toList());
        }
    };

    // 定义策略接口
    public abstract List<Stock> sort(List<Stock> source);
}

榜单实例RankServiceImpl

@Service
public class RankServiceImpl {

    /**
     * dataService.getSource() 提供原始的股票数据
     */
    @Resource
    private DataService dataService;

    /**
     * 前端传入榜单类型, 返回排序完的榜单
     *
     * @param rankType 榜单类型 形似 RankEnum.HighPrice.name()
     * @return 榜单数据
     */
    public List<Stock> getRank(String rankType) {
        // 获取策略,这里如果未匹配会抛 IllegalArgumentException异常
        RankEnum rank = RankEnum.valueOf(rankType);
        // 然后执行策略
        return rank.sort(dataService.getSource());
    }
}

在大批量 if-else的情况下,可使用策略枚举来消除替换。
总而言之,使用策略枚举可以很灵活处理各种复杂判断,且可读性与扩展性都比较好,它更像是函数式编程,即传进一个参数,就可以得到对应模式下返回的数值。
若 Java 里业务逻辑中大批量使用 if-else,则是面向过程了,因为业务逻辑里的 if-else 是从上往下一个 if 接一个 if 判断下去的,在各个 if 上打个断点,debug 下去,就明白它其实是面向过程的。

策略工厂

策略类添加了@Service注解,并指定了Service的value属性

/**
 * 高价榜
 * 注意申明 Service.value = HighPrice,他是我们的key,下同
 */
@Service("HighPrice")
public class HighPriceRank implements Strategy {

    @Override
    public List<Stock> sort(List<Stock> source) {
        return source.stream()
                .sorted(Comparator.comparing(Stock::getPrice).reversed())
                .collect(Collectors.toList());
    }
}

/**
 * 低价榜
 */
@Service("LowPrice")
public class LowPriceRank implements Strategy {

    @Override
    public List<Stock> sort(List<Stock> source) {
        return source.stream()
                .sorted(Comparator.comparing(Stock::getPrice))
                .collect(Collectors.toList());
    }
}

/**
 * 高涨幅榜
 */
@Service("HighRise")
public class HighRiseRank implements Strategy {

    @Override
    public List<Stock> sort(List<Stock> source) {
        return source.stream()
                .sorted(Comparator.comparing(Stock::getRise).reversed())
                .collect(Collectors.toList());
    }
}

利用spring自带beanFactory的优势,实现策略工厂

@Service
public class RankServiceImpl {

    /**
     * dataService.getSource() 提供原始的股票数据
     */
    @Resource
    private DataService dataService;
    /**
     * 利用注解@Resource和@Autowired特性,直接获取所有策略类
     * key = @Service的value
     */
    @Resource
    private Map<String, Strategy> rankMap;

    /**
     * 前端传入榜单类型, 返回排序完的榜单
     *
     * @param rankType 榜单类型 和Service注解的value属性一致
     * @return 榜单数据
     */
    public List<Stock> getRank(String rankType) {
        // 判断策略是否存在
        if (!rankMap.containsKey(rankType)) {
            throw new IllegalArgumentException("rankType not found");
        }
        // 获得策略实例
        Strategy rank = rankMap.get(rankType);
        // 执行策略
        return rank.sort(dataService.getSource());
    }
}

参考

https://cloud.tencent.com/developer/article/1969378
https://xie.infoq.cn/article/9369635b15f3f79981042d218

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值