前言

当代码中出现多重if-else语句或者switch语句时。
弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处,那么程序就会出错。
弊端之二:代码逻辑难以理解。

卫语句

卫语句(guard clauses) 特点是:其结果必须为真,程序才能执行下去,就是把复杂的条件表达式拆分成多个条件表达式,比如一个很复杂的表达式,嵌套了好几层的if-then-else语句,转换为多个if语句,实现它的逻辑,这多条的if语句就是卫语句。

卫语句的使用,并不能改变前言说的弊端:一旦需求更改,需要修改所有使用更改需求的if-else代码块。不过使用卫语句可以让自己或者代码维护人员很容易的了解代码的含义。

  1. public void findBoyfriend (Man man) {
  2. if (man.isUgly()) {
  3. System.out.println("本姑娘是外貌协会的资深会员");
  4. return;
  5. }
  6. if (man.isPoor()) {
  7. System.out.println("贫贱夫妻百事哀");
  8. return;
  9. }
  10. if (man.isBadTemper()) {
  11. System.out.println("银河有多远,你就给我滚多远");
  12. return;
  13. }
  14. System.out.println("可以先交往一段时间看看");
  15. }

策略模式

策略模式定义了一组算法,将它们逐个封装起来,并使它们可以相互替换。策略可以让算法独立于使用它们的客户而变化。

使用策略模式可以代替多重if-elseswitch语句,让代码维护变得更加简单。
策略模式UML:
大量if-else重构:卫语句、策略模式、状态模式 - 图1

  • 环境(Context)角色:持有一个Strategy的引用
  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现
  • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为

策略模式代码模板:

  1. package xyz.zeling.test.strategy.template;
  2. import xyz.zeling.test.strategy.template.base.Strategy;
  3. /**
  4. * @description 环境角色
  5. */
  6. public class Context {
  7. /**
  8. * 策略对象
  9. */
  10. private Strategy strategy;
  11. /**
  12. * @param strategy 具体策略对象
  13. */
  14. public Context(Strategy strategy) {
  15. this.strategy = strategy;
  16. }
  17. /**
  18. * @description 执行策略方法
  19. * @date 2018年1月14日 下午8:43:31
  20. */
  21. public void contextInterface() {
  22. strategy.strategyInterface();
  23. }
  24. }
  25. package xyz.zeling.test.strategy.template.base;
  26. /**
  27. * @description 抽象策略角色
  28. */
  29. public interface Strategy {
  30. /**
  31. * @description 策略方法
  32. * @date 2018年1月14日 下午8:41:00
  33. */
  34. void strategyInterface();
  35. }
  36. package xyz.zeling.test.strategy.template;
  37. import xyz.zeling.test.strategy.template.base.Strategy;
  38. /**
  39. * @description 具体策略类A
  40. */
  41. public class ConcreteStrategyA implements Strategy {
  42. @Override
  43. public void strategyInterface() {
  44. // TODO Auto-generated method stub
  45. }
  46. }
  47. package xyz.zeling.test.strategy.template;
  48. import xyz.zeling.test.strategy.template.base.Strategy;
  49. /**
  50. * @description 具体策略类B
  51. */
  52. public class ConcreteStrategyB implements Strategy {
  53. @Override
  54. public void strategyInterface() {
  55. // TODO Auto-generated method stub
  56. }
  57. }
  58. package xyz.zeling.test.strategy.template;
  59. import xyz.zeling.test.strategy.template.base.Strategy;
  60. /**
  61. * @description 具体策略类C
  62. */
  63. public class ConcreteStrategyC implements Strategy {
  64. @Override
  65. public void strategyInterface() {
  66. // TODO Auto-generated method stub
  67. }
  68. }

实例应用:水果有不同种类,每种水果对应不同价格,用策略模式实现。

  1. package xyz.zeling.test.strategy.demo.base;
  2. /**
  3. * @description 抽象策略,水果接口
  4. */
  5. public interface Fruit {
  6. /**
  7. * @description 输出对应价格
  8. * @date 2018年1月14日 下午3:03:07
  9. */
  10. void price();
  11. }
  12. package xyz.zeling.test.strategy.demo;
  13. import xyz.zeling.test.strategy.demo.base.Fruit;
  14. /**
  15. * @description 具体策略,苹果类
  16. */
  17. public class Apple implements Fruit {
  18. @Override
  19. public void price() {
  20. System.out.println("苹果的价格!");
  21. }
  22. }
  23. package xyz.zeling.test.strategy.demo;
  24. import xyz.zeling.test.strategy.demo.base.Fruit;
  25. /**
  26. * @description 具体策略,香蕉
  27. */
  28. public class Banana implements Fruit {
  29. @Override
  30. public void price() {
  31. System.out.println("香蕉的价格!");
  32. }
  33. }
  34. package xyz.zeling.test.strategy.demo;
  35. import xyz.zeling.test.strategy.demo.base.Fruit;
  36. /**
  37. * @description 具体策略,梨
  38. */
  39. public class Pear implements Fruit {
  40. @Override
  41. public void price() {
  42. System.out.println("梨的价格!");
  43. }
  44. }
  45. package xyz.zeling.test.strategy.demo;
  46. import xyz.zeling.test.strategy.demo.base.Fruit;
  47. /**
  48. * @description 策略环境,输出水果价格
  49. */
  50. public class FruitPrice {
  51. /**
  52. * 策略对象
  53. */
  54. private Fruit fruit;
  55. /**
  56. * @param fruit 策略对象
  57. */
  58. public FruitPrice(Fruit fruit) {
  59. this.fruit = fruit;
  60. }
  61. /**
  62. * @description 输出水果价格
  63. * @date 2018年1月14日 下午3:12:26
  64. */
  65. public void printFruitPrice() {
  66. fruit.price();
  67. }
  68. }
  69. 客户端
  70. /**
  71. * @description 使用策略模式:针对一组算法,将每一个算法封装到具有共同接口的独立的类
  72. */
  73. public static void useStrategy() {
  74. // 具体使用策略
  75. Fruit apple = new Apple();
  76. // 将策略放入环境中并执行策略
  77. new FruitPrice(apple).printFruitPrice();
  78. }

状态模式

状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

状态模式类图:
大量if-else重构:卫语句、策略模式、状态模式 - 图2

说明:

  • 环境(Context)角色,也成上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态
  • 抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为
  • 具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为

策略模式代码模板:

  1. package xyz.zeling.test.state.template.base;
  2. /**
  3. * @description 抽象状态角色
  4. */
  5. public interface State {
  6. /**
  7. * @description 处理方法
  8. */
  9. void handle();
  10. }
  11. package xyz.zeling.test.state.template;
  12. import xyz.zeling.test.state.template.base.State;
  13. /**
  14. * @description 具体状态类A
  15. */
  16. public class ConcreteStateA implements State {
  17. @Override
  18. public void handle() {
  19. // TODO Auto-generated method stub
  20. }
  21. }
  22. package xyz.zeling.test.state.template;
  23. import xyz.zeling.test.state.template.base.State;
  24. /**
  25. * @description 具体状态类B
  26. */
  27. public class ConcreteStateB implements State {
  28. @Override
  29. public void handle() {
  30. // TODO Auto-generated method stub
  31. }
  32. }
  33. package xyz.zeling.test.state.template;
  34. import xyz.zeling.test.state.template.base.State;
  35. /**
  36. * @description 具体状态类C
  37. */
  38. public class ConcreteStateC implements State {
  39. @Override
  40. public void handle() {
  41. // TODO Auto-generated method stub
  42. }
  43. }
  44. package xyz.zeling.test.state.template;
  45. import xyz.zeling.test.state.template.base.State;
  46. /**
  47. * @description 状态模式,环境角色类
  48. */
  49. public class Context {
  50. /**
  51. * 状态对象
  52. */
  53. private State state;
  54. /**
  55. * @description 设置状态
  56. * @date 2018年1月14日 下午9:13:20
  57. * @param state 具体状态
  58. */
  59. public void setState(State state) {
  60. this.state = state;
  61. }
  62. /**
  63. * @description 执行策略方法
  64. * @date 2018年1月14日 下午8:43:31
  65. */
  66. public void request() {
  67. state.handle();
  68. }
  69. }

实例应用:橘子有不同颜色,状态不同的橘子颜色不同,用状态模式实现。

  1. package xyz.zeling.test.state.demo.base;
  2. /**
  3. * @description 状态模式,状态接口
  4. */
  5. public interface Orange {
  6. /**
  7. * @description 输出橘子的颜色
  8. * @date 2018年1月14日 下午4:14:00
  9. */
  10. void printColor();
  11. }
  12. package xyz.zeling.test.state.demo;
  13. import xyz.zeling.test.state.demo.base.Orange;
  14. /**
  15. * @description 状态1:红色外观的橘子
  16. */
  17. public class RedOrange implements Orange {
  18. @Override
  19. public void printColor() {
  20. System.out.println("My color is red!");
  21. }
  22. }
  23. package xyz.zeling.test.state.demo;
  24. import xyz.zeling.test.state.demo.base.Orange;
  25. /**
  26. * @description 状态2:青色的橘子
  27. */
  28. public class CyanOrange implements Orange {
  29. @Override
  30. public void printColor() {
  31. System.out.println("My color is cyan!");
  32. }
  33. }
  34. package xyz.zeling.test.state.demo;
  35. import xyz.zeling.test.state.demo.base.Orange;
  36. /**
  37. * @description 状态3:黄色的橘子
  38. */
  39. public class YellowOrange implements Orange {
  40. @Override
  41. public void printColor() {
  42. System.out.println("My color is yellow!");
  43. }
  44. }
  45. package xyz.zeling.test.state.demo;
  46. import xyz.zeling.test.state.demo.base.Orange;
  47. /**
  48. * @description 橘子状态管理器
  49. */
  50. public class OrangeStateManage {
  51. /**
  52. * 橘子的状态
  53. */
  54. private Orange state;
  55. /**
  56. * @description 设置橘子的状态
  57. * @date 2018年1月14日 下午4:21:36
  58. * @param state
  59. */
  60. public void setState(Orange state) {
  61. this.state = state;
  62. }
  63. /**
  64. * @description 输出当前状态的橘子颜色
  65. * @date 2018年1月14日 下午4:22:08
  66. */
  67. public void print() {
  68. state.printColor();
  69. }
  70. }
  71. //客户端
  72. /**
  73. * @description 使用状态模式:状态模式,又称状态对象模式(Pattern of Objects for
  74. * States),
  75. 状态模式是对象的行为模式。状态模式允许一个对象在其内部状态改变的时候改变其行为。
  76. 这个对象看上去就像是改变了它的类一样
  77. */
  78. public static void useState() {
  79. // 具体使用状态
  80. Orange cyanOrange = new CyanOrange();
  81. // 创建环境
  82. OrangeStateManage orangeStateManage = new OrangeStateManage();
  83. // 设置状态并执行
  84. orangeStateManage.setState(cyanOrange);
  85. orangeStateManage.print();
  86. }

策略模式和状态模式的比较

  • 状态模式:这个模式就好比员工申请离职单的流程,离职单到直接上级,这个状态就是直接上级批示,等直接上级审阅之后,通过了就到下一个状态。这一个个状态对应不同的处理,这是有顺序要求的。
  • 策略模式:这个模式好比于你假期要出国游玩,有日本、美国、新加坡等国家,你每到一个国家就执行不同的游玩策略,可以先去日本,也可以先去美国,没有顺序要求。