[关闭]
@linux1s1s 2017-01-05T09:36:37.000000Z 字数 4499 阅读 1375

Base Time-Design Patterns-Strategy

Base 2017-01


需求

简单实现(一次性代码)

MallardDuck.java

  1. public class MallardDuck {
  2. /**
  3. * 鸭子叫的方法
  4. */
  5. public void quack() {
  6. //TODO
  7. }
  8. /**
  9. * 游泳方法
  10. */
  11. public void swim() {
  12. //TODO
  13. }
  14. /**
  15. * 外形方法,比如鸭子的颜色
  16. */
  17. public void display() {
  18. //TODO
  19. }
  20. }

RedheadDuck.java

  1. public class RedheadDuck {
  2. /**
  3. * 鸭子叫的方法
  4. */
  5. public void quack() {
  6. //TODO
  7. }
  8. /**
  9. * 游泳方法
  10. */
  11. public void swim() {
  12. //TODO
  13. }
  14. /**
  15. * 外形方法,比如鸭子的颜色
  16. */
  17. public void display() {
  18. //TODO
  19. }
  20. }

我们发现如果quack()swim()相同,那么就重复写了两遍相同的代码,增加维护成本的同时也丝毫没有体现出面向对象的思想,所以很自然的将相同的方法抽取到基类里面去。

面向对象实现

先给个UML图直观表达一下:
此处输入图片的描述
然后看看类的实现:

基类Duck.java

  1. public abstract class Duck {
  2. /**
  3. * 鸭子叫的方法
  4. */
  5. public void quack() {
  6. //TODO
  7. }
  8. /**
  9. * 游泳方法
  10. */
  11. public void swim() {
  12. //TODO
  13. }
  14. /**
  15. * 外形方法,比如鸭子的颜色,交给基类去实现
  16. */
  17. public abstract void display();
  18. }

子类MallardDuck.java

  1. public class MallardDuck extends Duck {
  2. /**
  3. * 外形方法,比如鸭子的颜色
  4. */
  5. @Override
  6. public void display() {
  7. //MallardDuck TODO
  8. }
  9. }

子类RedheadDuck.java

  1. public class RedheadDuck extends Duck {
  2. /**
  3. * 外形方法,比如鸭子的颜色
  4. */
  5. @Override
  6. public void display() {
  7. //RedheadDuck TODO
  8. }
  9. }

现在需求突然改动,需要增加一个鸭子的飞翔功能,那么这时我们很庆幸抽取了基类,我们在基类里面增加一个飞翔的方法即可如下:
基类Duck.java

  1. public abstract class Duck {
  2. /**
  3. * 鸭子叫的方法
  4. */
  5. public void quack() {
  6. //TODO
  7. }
  8. /**
  9. * 游泳方法
  10. */
  11. public void swim() {
  12. //TODO
  13. }
  14. /**
  15. * 飞翔方法
  16. */
  17. public void fly(){
  18. //TODO
  19. }
  20. /**
  21. * 外形方法,比如鸭子的颜色,交给基类去实现
  22. */
  23. public abstract void display();
  24. }

如果产品经理又改动了,红嘴鸭子不能飞,这个时候突然意识到自己在基类里面做多了事情,只能在子类中重写基类的fly()方法来补救。

对于上面的设计,你可能发现一些弊端,如果基类有新的特性,子类都必须变动,这是我们开发最不喜欢看到的,一个类变让另一个类也跟着变,这有点不符合OO设计了。这样很显然的耦合了一起。利用继承-->耦合度太高了.

面向接口实现

针对上面的问题,我们把容易引起变化的部分提取出来并封装之,来应付以后的变化。虽然代码量加大了,但可用性提高了,耦合度也降低了。

容易变化的部分是quack()fly()方法,需求很可能从这两个功能变化,所以提取出来两个接口,分别是Flyable.javaQuackable.java,然后基类Duck.java也需要相应的改变,如下所示:

此处输入图片的描述

Flyable.java

  1. public interface Flyable {
  2. void fly();
  3. }

Quackable.java

  1. public interface Quackable {
  2. void quack();
  3. }

基类Duck.java

  1. public abstract class Duck {
  2. /**
  3. * 游泳方法
  4. */
  5. public void swim() {
  6. //TODO
  7. }
  8. /**
  9. * 外形方法,比如鸭子的颜色,交给基类去实现
  10. */
  11. public abstract void display();
  12. }

子类MallardDuck.java

  1. public class MallardDuck extends Duck implements Quackable {
  2. @Override
  3. public void display() {
  4. //TODO MallardDuck display
  5. }
  6. @Override
  7. public void quack() {
  8. //TODO MallardDuck quack
  9. }
  10. }

子类RedheadDuck.java

  1. public class RedheadDuck extends Duck implements Flyable, Quackable {
  2. @Override
  3. public void display() {
  4. //TODO RedheadDuck display
  5. }
  6. @Override
  7. public void fly() {
  8. //TODO RedheadDuck fly
  9. }
  10. @Override
  11. public void quack() {
  12. //TODO RedheadDuck quack
  13. }
  14. }

对上面各方式的总结:

- 继承的好处:让共同部分,可以复用.避免重复编程.
- 继承的不好:耦合性高.一旦超类添加一个新方法,子类都继承,拥有此方法,若子类相当部分不实现此方法,则要进行大批量修改.继承时,子类就不可继承其它类了.
- 接口的好处:解决了继承耦合性高的问题.且可让实现类,继承或实现其它类或接口.
- 接口的不好:不能真正实现代码的复用.可用以下的策略模式来解决.

典型策略实现

我们有一个设计原则:
找出应用中相同之处,且不容易发生变化的东西,把它们抽取到抽象类中,让子类去继承它们;
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

现在,为了要分开“变化和不变化的部分”,我们准备建立两组类(完全远离Duck类),一个是"fly"相关的,另一个
是“quack”相关的,每一组类将实现各自的动作。比方说,我们可能有一个类实现“呱呱叫”,另一个类实现“吱吱
叫”,还有一个类实现“安静”。
先来看看UML图:

此处输入图片的描述

接下来看源码:

FlyBehavior.java

  1. public interface FlyBehavior {
  2. void fly();
  3. }

QuackBehavior.java

  1. public interface QuackBehavior {
  2. void quack();
  3. }

接着是一组实现FlyBehavior接口的具体实现类
FlyWithWings.java

  1. public class FlyWithWings implements FlyBehavior {
  2. @Override
  3. public void fly() {
  4. //有翅膀的飞行
  5. }
  6. }

FlyWithNoWings.java

  1. public class FlyWithNoWings implements FlyBehavior {
  2. @Override
  3. public void fly() {
  4. //没有翅膀的飞行
  5. }
  6. }

然后是一组实现QuackBehavior接口的具体实现类

CroaksQuack.java

  1. public class CroaksQuack implements QuackBehavior {
  2. @Override
  3. public void quack() {
  4. //实现呱呱叫的鸭子
  5. }
  6. }

SqueakQuack.java

  1. public class SqueakQuack implements QuackBehavior {
  2. @Override
  3. public void quack() {
  4. //实现吱吱叫的鸭子
  5. }
  6. }

MuteQuack.java

  1. public class MuteQuack implements QuackBehavior {
  2. @Override
  3. public void quack() {
  4. //实现不会叫的鸭子
  5. }
  6. }

基类聚合了上面的两个接口,成员变量的实例化可以通过构造器,也可以通过Set方法实现。通过这种聚合方式,我们发现不仅大大降低了耦合度,还最大化复用了代码。
Duck.java

  1. public abstract class Duck {
  2. protected FlyBehavior mFly;
  3. protected QuackBehavior mQuack;
  4. public Duck() {
  5. }
  6. public Duck(FlyBehavior fly, QuackBehavior quack) {
  7. mFly = fly;
  8. mQuack = quack;
  9. }
  10. /**
  11. * 鸭子叫的方法
  12. */
  13. public void quack() {
  14. if (mQuack != null) {
  15. mQuack.quack();
  16. }
  17. }
  18. /**
  19. * 飞翔方法
  20. */
  21. public void fly() {
  22. if (mFly != null) {
  23. mFly.fly();
  24. }
  25. }
  26. /**
  27. * 游泳方法
  28. */
  29. public void swim() {
  30. //TODO
  31. }
  32. /**
  33. * 外形方法,比如鸭子的颜色,交给基类去实现
  34. */
  35. public abstract void display();
  36. /**
  37. * Get Set 方法
  38. *
  39. * @return
  40. */
  41. public FlyBehavior getFly() {
  42. return mFly;
  43. }
  44. public void setFly(FlyBehavior fly) {
  45. mFly = fly;
  46. }
  47. public QuackBehavior getQuack() {
  48. return mQuack;
  49. }
  50. public void setQuack(QuackBehavior quack) {
  51. mQuack = quack;
  52. }
  53. }

然后是两个子类
MallardDuck.java

  1. public class MallardDuck extends Duck {
  2. @Override
  3. public void display() {
  4. //TODO MallardDuck display
  5. }
  6. }

RedheadDuck.java

  1. public class RedheadDuck extends Duck {
  2. @Override
  3. public void display() {
  4. //TODO RedheadDuck display
  5. }
  6. }

最后看一下TestCase

  1. public class TestCase {
  2. public static void main(String[] args) {
  3. Duck duckFly = new MallardDuck();
  4. duckFly.setFly(new FlyWithWings());
  5. duckFly.setQuack(new CroaksQuack());
  6. Duck duckNoFly = new RedheadDuck();
  7. duckNoFly.setFly(new FlyWithNoWings());
  8. duckNoFly.setQuack(new SqueakQuack());
  9. duckFly.fly();
  10. duckFly.quack();
  11. duckNoFly.fly();
  12. duckNoFly.quack();
  13. }
  14. }

参考博文
java常用设计模式 本文做了适当编辑,特别说明。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注