模式说明

处理同样的事情可以有多种方法,例如排序数组,可以用冒泡,选择,插入,希尔,快速等等排序方法,对于同样的输入有多种策略能来处理并输出不同策略处理的结果。在程序中使用某一种策略,最直接的方式是在在客户端中将不同的策略写入if-elseswitch-case的分支,然后由客户端进行选择。缺点是客户端职责过大,违背单一职责原则,其次是某个策略变动需要修改客户端代码,违背开闭原则。针对此类场景可以使用策略模式。每一种策略单独成类,均继承一个抽象策略类,设置一个持有策略类的环境,客户端不必了解每个策略细节,而是声明某个具体策略后,借由上下文类来执行其中的策略动作。如需增减策略,只需要增减该策略的类。一个已存在的策略出现变动,也只需要修改该策略本身,增删改都不会影响已有程序,满足开闭原则。一个策略类实现一种策略方法,满足单一职责原则。

本示例以处理收款为例,演示客户端如何通过环境类来指定某种收款策略得到应收结果。

结构

抽象策略类
定义一个收款抽象方法acceptCash
具体策略类
继承抽象策略类,实现具体策略。
环境类(上下文类)
持有一个收费策略类,维护一个收款方法,该方法内实际调用收费策略类的收款方法。

代码演示

  1. package com.yukiyama.pattern.behavior;
  2. /**
  3. * 策略模式
  4. */
  5. public class StrategyDemo {
  6. public static void main(String[] args) {
  7. // 声明收费具体策略类cash1,按85折收费
  8. Cash cash1 = new CashDiscount(0.15);
  9. // 声明收费具体策略cash2,正常收费
  10. Cash cash2 = new CashNormal();
  11. // 声明收费上下文,通过构造器初始化收费策略为cash1
  12. CashContext cc1 = new CashContext(cash1);
  13. // 声明收费上下文,通过构造器初始化收费策略为cash2
  14. CashContext cc2 = new CashContext(cash2);
  15. // 付款100,返回cash1策略的应收费用
  16. double requestMoney1 = cc1.acceptCash(100);
  17. // 输出“85.0”
  18. System.out.println(requestMoney1);
  19. // 付款100,返回cash1策略的应收费用,输出“100”
  20. double requestMoney2 = cc2.acceptCash(100);
  21. // 输出“100.0”
  22. System.out.println(requestMoney2);
  23. }
  24. }
  25. /**
  26. * 抽象策略类
  27. * 定义一个收款抽象方法acceptCash,入参是客户付款,返回应收费用
  28. */
  29. abstract class Cash{
  30. public abstract double acceptCash(double money);
  31. }
  32. /**
  33. * 具体策略类
  34. * 继承抽象策略类,实现具体策略。
  35. * 下例是按原价收费。
  36. */
  37. class CashNormal extends Cash{
  38. @Override
  39. public double acceptCash(double money) {
  40. return money;
  41. }
  42. }
  43. /**
  44. * 具体策略类
  45. * 下例是按折扣收费,折扣在声明具体Cash类时通过构造器传入并初始化。
  46. */
  47. class CashDiscount extends Cash{
  48. private double rate;
  49. public CashDiscount(double discount) {
  50. rate = 1.0 - discount;
  51. }
  52. @Override
  53. public double acceptCash(double money) {
  54. return money * rate;
  55. }
  56. }
  57. /**
  58. * 环境类(上下文类)
  59. * 持有一个收费策略类,并通过有参构造器传入收费策略实例初始化。
  60. * 维护一个收款方法,内部调用其所持有的具体收费策略的收款方法。
  61. */
  62. class CashContext{
  63. private Cash cash;
  64. public CashContext(Cash cash) {
  65. this.cash = cash;
  66. }
  67. public double acceptCash(double money) {
  68. return cash.acceptCash(money);
  69. }
  70. }