对策略模式的理解

目录

一、场景

  • 程序员除了会ctrl+ c + ctrl + v之外,最擅长的莫过于写if...else...了,哈哈:)
  • 有些场景下,我们可以预料到,会写非常多的if...else if...else if ... else if ...。这时候,可以使用策略模式。

1、题目描述 【来源

小明家的超市推出了不同的购物优惠策略,你可以根据自己的需求选择不同的优惠方式。其中,有两种主要的优惠策略:

  1. 九折优惠策略:原价的90%。
  2. 满减优惠策略:购物满一定金额时,可以享受相应的减免优惠。
    具体的满减规则如下:

满100元减5元

满150元减15元

满200元减25元

满300元减40元
请你设计一个购物优惠系统,用户输入商品的原价和选择的优惠策略编号,系统输出计算后的价格。

2、输入描述

输入的第一行是一个整数 N(1 ≤ N ≤ 20),表示需要计算优惠的次数。

接下来的 N 行,每行输入两个整数,第一个整数M( 0 < M < 400) 表示商品的价格, 第二个整数表示优惠策略,1表示九折优惠策略,2表示满减优惠策略

3、输出描述

每行输出一个数字,表示优惠后商品的价格

4、输入示例

4

100 1

200 2

300 1

300 2

5、输出示例

90

175

270

260

二、不使用策略模式

  • 代码:
java 复制代码
public class Application {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();

        for (int i = 0; i < n; i++) {
            int price = scanner.nextInt();
            int strategyNum = scanner.nextInt();

            if (strategyNum == 1) {
                System.out.println((int) (price * 0.9));
            } else if (strategyNum == 2) {
                if (price < 100) {
                    System.out.println(price);
                } else if (price < 150) {
                    // 满100元减5元
                    System.out.println(price - 5);
                } else if (price < 200) {
                    // 满150元减15元
                    System.out.println(price - 15);
                } else if (price < 300) {
                    // 满200元减25元
                    System.out.println(price - 25);
                } else {
                    // 满300元减40元
                    System.out.println(price - 40);
                }

            } else {
                throw new RuntimeException("Invalid strategy number");
            }
        }

    }
}
  • 问题:
    • 可以预见的是,随着发展,优惠策略肯定不止一种。
    • 因此,可以用策略模式优化代码。

三、使用策略模式

1、不优雅的实现

  • 策略
java 复制代码
public interface Strategy<T> {
    void execute(T data);
}

@AllArgsConstructor
public class DiscountStrategy implements Strategy<Integer> {
    private Double discount;

    @Override
    public void execute(Integer data) {
        System.out.println((int) (data * discount));
    }
}

public class FullMinusStrategy implements Strategy<Integer> {
    @Override
    public void execute(Integer price) {
        if (price < 100) {
            System.out.println(price);
        } else if (price < 150) {
            // 满100元减5元
            System.out.println(price - 5);
        } else if (price < 200) {
            // 满150元减15元
            System.out.println(price - 15);
        } else if (price < 300) {
            // 满200元减25元
            System.out.println(price - 25);
        } else {
            // 满300元减40元
            System.out.println(price - 40);
        }
    }
}
  • 客户端
java 复制代码
public class Application {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();

        Strategy strategy = null;
        for (int i = 0; i < n; i++) {
            int price = scanner.nextInt();
            int strategyNum = scanner.nextInt();

            switch (strategyNum) {
                case 1:
                    strategy = new DiscountStrategy(0.9D);
                    break;
                case 2:
                    strategy = new FullMinusStrategy();
                    break;
                default:
                    new RuntimeException("Strategy not found");
            }

            strategy.execute(price);
        }
    }
}
  • 啊这,这不还是写if...esle吗?(switch只是对if...else的美化而已)
  • 我们其实已经知道<1, DiscountStrategy>, <2, FullMinusStrategy>,因此,可以提前建立联系。

2、策略模式 + 简单工厂模式

2.1 代码

  • 策略和上面一样
java 复制代码
public interface Strategy<T> {
    ...
}

@AllArgsConstructor
public class DiscountStrategy implements Strategy<Integer> {
    ...
}

public class FullMinusStrategy implements Strategy<Integer> {
    ...
}
  • 对策略的封装:
java 复制代码
public class StrategyManager {
    private static final Map<Integer, Strategy> strategies = new HashMap<>();

    public StrategyManager() {
        strategies.put(1, new DiscountStrategy(0.9D));
        strategies.put(2, new FullMinusStrategy());
    }

    public void execute(Integer strategyNum, Integer data) {
        Strategy strategy = strategies.get(strategyNum);
        if (strategy == null) {
            throw new RuntimeException("Strategy not found");
        }

        strategy.execute(data);
    }
}
  • 客户端:
java 复制代码
public class Application {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();

        StrategyManager strategyManager = new StrategyManager();
        for (int i = 0; i < n; i++) {
            int price = scanner.nextInt();
            int strategyNum = scanner.nextInt();

            strategyManager.execute(strategyNum, price);
        }
    }
}

2.2 优点

  • 即使再增加一种策略,也不用修改客户端的代码。
  • 然而,没有Spring的辅助下,需要自己构建Map<Integer, Strategy> strategies,这使得增加一种策略时,还得修改StrategyManager类。

2.3 另一种实现方式

  • 策略:
java 复制代码
public interface Strategy<T, R> {
    boolean match(R matchContext);
    void execute(T data);
}

@AllArgsConstructor
public class DiscountStrategy implements Strategy<Integer, Integer> {
    private Double discount;

    @Override
    public boolean match(Integer matchContext) {
        return matchContext == 1;
    }

    @Override
    public void execute(Integer data) {
        System.out.println((int) (data * discount));
    }
}

public class FullMinusStrategy implements Strategy<Integer, Integer> {
    @Override
    public boolean match(Integer matchContext) {
        return matchContext == 2;
    }

    @Override
    public void execute(Integer price) {
        if (price < 100) {
            System.out.println(price);
        } else if (price < 150) {
            // 满100元减5元
            System.out.println(price - 5);
        } else if (price < 200) {
            // 满150元减15元
            System.out.println(price - 15);
        } else if (price < 300) {
            // 满200元减25元
            System.out.println(price - 25);
        } else {
            // 满300元减40元
            System.out.println(price - 40);
        }
    }
}
  • 对策略的封装:
java 复制代码
public class StrategyManager {
//    private static final Map<Integer, Strategy> strategies = new HashMap<>();
    private static final List<Strategy> strategies = new ArrayList<>();

    public StrategyManager() {
        strategies.add(new DiscountStrategy(0.9D));
        strategies.add(new FullMinusStrategy());
    }

    public void execute(Integer strategyNum, Integer data) {
        Strategy strategy = strategies.stream()
                .filter(s -> s.match(strategyNum))
                .findFirst()
                .orElse(null);
        if (strategy == null) {
            throw new RuntimeException("Strategy not found");
        }

        strategy.execute(data);
    }
}
  • 客户端和上面一样。

个人觉得这种方式更灵活。

四、个人思考

  • 当遇到写非常多的if...else if...else if ... else if ...时,说明<规则,处理>是确定的。
  • 那么完全可以抽象为:
java 复制代码
public interface Strategy<T, R> {
    boolean match(R matchContext); // 匹配规则
    void execute(T data); // 处理数据
}

各种具体的xxxStrategy
java 复制代码
@Component
public class StrategyManager {
    @Autowired
    private List<Strategy> strategies;

    public void execute(Integer strategyNum, Integer data) {
        Strategy strategy = strategies.stream()
                .filter(s -> s.match(strategyNum))
                .findFirst()
                .orElse(null);
        if (strategy == null) {
            throw new RuntimeException("Strategy not found");
        }

        strategy.execute(data);
    }
}
  • 小插曲,后续会写文章解释。
java 复制代码
@Component
public class StrategyManager {
	// 字段注入,不支持静态变量。
    @Autowired
    private static List<Strategy> strategies;
	...
}
相关推荐
tiger从容淡定是人生18 小时前
可审计性:AI时代自动化测试的核心指标
人工智能·自动化·项目管理·策略模式·可用性测试·coo
都说名字长不会被发现2 天前
模版方法 + 策略模式在库存增加/扣减场景下的应用
策略模式·模板方法模式·宏命令·策略聚合·库存设计
默|笙2 天前
【Linux】进程概念与控制(2)_进程控制
java·linux·策略模式
枫叶林FYL3 天前
Agent/Teakenote 系统(Swarm 架构)深度技术报告
架构·策略模式
苏渡苇4 天前
枚举的高级用法——用枚举实现策略模式和状态机
java·单例模式·策略模式·枚举·状态机·enum
harder3216 天前
Swift 面向协议编程的 RMP 模式
开发语言·ios·mvc·swift·策略模式
skywalk81637 天前
esxi8 虚拟机中怎么安装mac os(纯AI回答,未实践)
策略模式·esxi
廖圣平7 天前
从零开始,福袋直播间脚本研究【八】《策略模式》
开发语言·python·bash·策略模式
爱学习 爱分享11 天前
简单工厂模式和策略模式的区别
简单工厂模式·策略模式
xcntime14 天前
Python中print函数如何实现不换行输出?
策略模式