• JAVA设计模式之策略模式


    开发一款游戏,里面有各种鸭子,这些鸭子有共同点:会游泳、会叫;

    1.设计超类Duck,里面有swim()方法和quack()方法,所有鸭子继承此超类,那么继承的对象便都有了游泳和叫的技能;

     1 public abstract class Duck {
     2     /*所有的鸭子都会游泳*/
     3     public void swim() {
     4         System.out.println("I can swim");
     5     }
     6     /*所有的鸭子都会叫*/
     7     public void quack() {
     8         System.out.println("I can quack");
     9     }
    10     /*其它行为*/
    11     public abstract void display();
    12 }

    2.需求变更:增加三种叫的方法,不同的鸭子叫声不同,有“吱吱叫”、“呱呱叫”,还有不会叫;那么可以覆写每个子类quack()方法,使鸭子叫声不同。

    3.需求变更:鸭子得会飞;那么可以为超类Duck增加fly()方法,所有继承的子类便有了飞的技能;

    4.需求变更:有些鸭子不会飞;那么覆写每个鸭子的fly()方法,使不会飞的鸭子不能飞;

    这里已经存在一些问题:每当有新的鸭子出现,都需要检查fly()方法和quack()方法,确认是否需要覆写。这种继承 and 覆写的方法很糟糕。

    改变:

    继承或许不可取,改用接口;可以把fly和quack提取为两个接口,会飞的实现flyable接口,会叫的实现quackable接口;

    改用接口带来的麻烦:每个实现flyable或quackable的子类,都需要去覆写fly()方法和quack()方法,代码根本不能复用,导致做很多重复工作。

    问题分析:鸭子的fly和quack的行为在子类中不断的变化,让子类都具有这些行为是不恰当的;使用flyable和quackable接口,虽然可以解决一部分问题,但是fly()和quack()方法需在每个子类中覆写,代码不能复用,会增加很多重复工作。

    有种设计原则很适合这样的情况:

    一、找出需求中可能变化之处,把它们独立出来,不和不变的代码混在一起;(可以把易于变化的代码封装起来,以便可以轻易的改动和扩充此部分,而不影响不需要改动的其它部分)。

    二、针对接口编程而非针对实现。

    三、多用组合,少用继承。

    开始将fly行为和quack行为独立出来:

    准备组建两组类(完全脱离Duck),一组是fly相关的(实现FlyBehavior接口),一组是quack相关的(实现QuackBehavior接口)。有:会飞(FlyWithWings)、不会飞(FlyNoWay)、呱呱叫(Quack)、吱吱叫(Squeak)、安静(MuteQuack)等类;

     1 /*会飞*/
     2 public class FlyWithWings implements FlyBehavior {
     3 
     4     public void fly() {
     5         System.out.println("I'm flying.");
     6     }
     7 
     8 }
     9 /*不会飞*/
    10 public class FlyNoWay implements FlyBehavior {
    11 
    12     public void fly() {
    13         System.out.println("I can't fly.");
    14     }
    15 
    16 }
    17 /*呱呱叫*/
    18 public class Quack implements QuackBehavior {
    19 
    20     public void quack() {
    21         System.out.println("呱!呱!");
    22     }
    23 
    24 }
    25 /*吱吱叫*/
    26 public class Squeak implements QuackBehavior {
    27 
    28     public void quack() {
    29         System.out.println("吱!吱!");
    30     }
    31 
    32 }
    33 /*不会叫*/
    34 public class MuteQuack implements QuackBehavior {
    35 
    36     public void quack() {
    37         System.out.println("<< Silence >>");
    38     }
    39 
    40 }

    新设计中,鸭子的子类将使用接口(FlyBehavior和QuackBehavior)所表示的行为,子类不需要去实现特定的行为(之前的设计,每个子类需要实现不同的功能覆写,把子类与实现绑定),而是由FlyBehavior和QuackBehavior的实现类来实现特定的行为。

    整合鸭子的行为:

     1 public abstract class Duck {
     2     
     3     public FlyBehavior flyBehavior;
     4     public QuackBehavior quackBehavior;
     5     
     6     //可以动态的设定既成的行为
     7     public void setFlyBehavior(FlyBehavior flyBehavior) {
     8         this.flyBehavior = flyBehavior;
     9     }
    10     public void setQuackBehavior(QuackBehavior quackBehavior) {
    11         this.quackBehavior = quackBehavior;
    12     }
    13     
    14     /**
    15      * 其它行为
    16      */
    17     public abstract void display();
    18     
    19     /**
    20      * 能够表演飞和叫的行为
    21      */
    22     public void performFly() {
    23         flyBehavior.fly();
    24     }
    25     public void performQuack() {
    26         quackBehavior.quack();
    27     }
    28     
    29     /**
    30      * 所有鸭子都会游泳
    31      */
    32     public void swim() {
    33         System.out.println("I can swim.");
    34     }
    35 }

    鸭子对象不亲自处理quack和fly的行为,而是由QuackBehavior和FlyBehavior接口的对象去处理。并且子类也可动态设定飞和叫的行为。

     子类鸭子(绿头鸭)代码实例:

     1 /**
     2  * 绿头鸭
     3  *
     4  */
     5 public class MallardDuck extends Duck {
     6     
     7     public MallardDuck() {
     8         flyBehavior = new FlyWithWings();
     9         quackBehavior = new Quack();
    10     }
    11     
    12     @Override
    13     public void display() {
    14         System.out.println("I'm a real mallard duck!");
    15     }
    16 
    17 }

    测试代码的行为:

     1 public class DuckTest {
     2 
     3     @Test
     4     public void test() {
     5         Duck mallard = new MallardDuck();
     6         mallard.swim();
     7         mallard.display();
     8         //行为没有被改变之前
     9         mallard.performQuack();
    10         mallard.performFly();
    11         //行为改变之后
    12         mallard.setQuackBehavior(new Squeak());
    13         mallard.setFlyBehavior(new FlyNoWay());
    14         mallard.performQuack();
    15         mallard.performFly();
    16         
    17     }
    18 }

    运行结果:

    I can swim.
    I'm a real mallard duck!
    呱!呱!
    I'm flying.
    吱!吱!
    I can't fly.

    本例中将鸭子与两种行为QuackBehavior和FlyBehavior接口类组合使用,弹性大,且易于维护。

  • 相关阅读:
    C#过滤重复数据,使用泛型
    office2007:您正试图运行的函数包含有宏或需要宏语言支持的内容。而在安装此软件时,您(或您的管理员)选择了不安装宏或控件的支持功能
    InstallShield高级应用获取本机所有的SQL服务
    结对项目
    ActiveMQ 初学1:ActiveMQ 创建连接对象
    【JVM】jstack和dump线程分析(2)
    【JVM】jstack 查询占用最大资源线程|排查死循环等
    【java多线程】volatile 关键字
    1.zookeeper原理解析数据存储之Zookeeper内存结构
    【数据算法】Java实现二叉树存储以及遍历
  • 原文地址:https://www.cnblogs.com/bearduncle/p/8023428.html
Copyright © 2020-2023  润新知