Head First 设计模式学习笔记 ——策略模式

Head First 设计模式学习 ——策略模式简单理解

    问题来源:
    在子类继承父类时,如果子类不需要使用父类中的某些方法,该如何处理?
    举个例子,如下图所示,父类Duck类抽象了现实世界中的鸭子,拥有quack()(鸣叫)、swim()(游泳)和fly()(飞行)成员方法;子类WildDuck(野鸭)、WoodDuck(木头鸭)均继承Duck类。由于木头鸭子不会叫、也不会飞,因此WoodDuck继承Duck类的quack()方法和fly()方法是没有意义的。针对这一问题,有三种解决方法。


    方法一:
    覆盖WoodDuck类中的quack()和fly()方法,让这两个方法什么都不做,即:
public void quack() {
    //此处什么都不做
}
public void fly() {
    //此处什么都不做
}
    这样就使WoodDuck类中的quack()方法和fly()方法不起作用,保证木头鸭子不能飞也不能叫。但是这种方法也存在一些问题,当有新的子类加入,使用者需要不断检查是否覆盖quack()方法和fly()方法,显然当新加入的子类数量庞大时,这种方法的工作量也会随之增长。
    方法二:
    将“飞行”和“鸣叫”抽取出来,形成两个接口,如下如所示:
    
     
    本着“谁用接口谁实现的原则”,需要“飞行”的鸭子就实现“飞行”接口;需要“鸣叫”的鸭子就实现“鸣叫”接口;既需要“飞行”又需要“鸣叫”的鸭子就同时将两个接口实现;不需要“飞行”也不需要“鸣叫”的鸭子就不用实现这两个接口。
    然而,这种方法的问题在于,每一个鸭子都需要重写quack()方法和fly()方法,使得重复的代码会变多,没有做到quack()方法和fly()方法的代码复用。举个例子:假设鸭子的飞行需要扇动翅膀、盘旋上升和保持平衡3个步骤。同时假设有红头鸭和绿头鸭两种鸭子,则红头鸭的fly()方法可以写为:
public fly() { //红头鸭
    扇动翅膀();
    盘旋上升();
    保持平衡();
}
若此时新来一个子类绿头鸭,由于绿头鸭也会飞,所以绿头鸭的fly()方法也可以写为:
public fly() { //绿头鸭
    扇动翅膀();
    盘旋上升();
    保持平衡();
}
观察上面两个fly()方法,可以发现绿头鸭的fly()方法和红头鸭的fly()方法存在着代码重复的问题。此外,飞行的行为可能不止一种,比如有的鸭子飞行不需要盘旋上升,而有的鸭子需要盘旋上升和扇动翅膀互相结合等等等等。这些问题需要子类根据实际情况重写fly()方法,因而存在代码重复和工作量大的问题。
    方法三:
    在方法二的基础上,构建两个类的集合,分别封装“飞行”行为和“鸣叫”行为,如下图所示:

   

    所有的“飞行”行为都需要实现FlyBehavior接口,所有的“鸣叫”行为都需要实现QuackBehavior接口。对于WildDuck和WoodDuck来说,无需实现FlyBehavior和QuackBehavior,只需继承Duck类,并使用flyBehavior和quackBehavior实例化行为即可。

    完整的代码如下:
    FlyBehavior.java:
public interface FlyBehavior {
    public void fly();
}
    QuackBehavior.java:
public interface QuackBehavior {
    public void quack();
}
     Fly1.java:
public class Fly1 implements FlyBehavior {
    public void fly() {
        System.out.println("~~~I can fly~~~");
    }
}
        Fly2.java:
public class Fly2 implements FlyBehavior {
    public void fly() {
        System.out.println("~~~I can't fly~~~");
    }
}
     Quack1.java:
public class Quack1 implements QuackBehavior {
    public void quack() {
        System.out.println("~~~I can quack~~~");
    }
}
       Quack2.java:
public class Quack2 implements QuackBehavior {
    public void quack() {
        System.out.println("~~~I can't quack~~~");
    }
}
       Duck.java:
public class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
	
    public Duck() {}
	
    public void swim() {
        System.out.println("~~~swim~~~");
    }
	
    public void performQuack() {
        quackBehavior.quack();
    }
	
    public void performFly() {
        flyBehavior.fly();
    }
}
        WildDuck.java:
public class WildDuck extends Duck {
    public WildDuck() {
        flyBehavior = new Fly1();
        quackBehavior = new Quack1();
    }
}
       WoodDuck.java:
public class WoodDuck extends Duck {
    public WoodDuck() {
        flyBehavior = new Fly2();
        quackBehavior = new Quack2();
    }
}
       test.java:
public class test {
	
    public static void main(String[] args) {
		
        //野鸭子
        WildDuck wildDuck = new WildDuck();
        wildDuck.performFly();
        wildDuck.performQuack();
		
        //木头鸭子
        WoodDuck woodDuck = new WoodDuck();
        woodDuck.performFly();
        woodDuck.performQuack();
    }	
}

    使用javac命令编译上述java代码后,用java命令运行test.java,结果如下图所示:


    经过上面的实验后,给出《HeadFirst设计模式》一书对于策略模式的定义:策略模式定义了算法族,分别封装起来,让他们之间可以相互替换使用,此模式让算法的变换独立于使用算法的客户。在上面的实验中,飞行类封装了Fly1和Fly2两个类,鸣叫类封装了Quack1和Quack2两个类,WildDuck和WoodDuck(客户)可以在程序中指定使用Fly1还是Fly2,使用Quack1还是Quack2。使用这种模式可以很方便地添加新的行为如:Fly3、Fly4....。
   
    参考自:
    


 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值