设计模式之策略模式

《Head First 设计模式》,好书!

策略模式经典的例子:鸭子模型

假设我们主要研究鸭子(DUCK)的"叫(quack)"和鸭子的飞(fly)。

public abstract class Duck{
  //呱呱
  quack();
  //
   fly();
}

      1.假如,我们想要很多种鸭子在屏幕上飞和叫,想到了设计一个DUCK父类,让许许多多的鸭子子类继承fly()和quack()。这样鸭子们就可以自由的在屏幕飞和叫了。

      2.问题来了,屏幕上有个RubberDuck(橡皮鸭)在飞和呱呱叫,这怎么行!RubberDuck不会飞并且叫声是"吱吱"而不是"呱呱"。于是灵机一动,把RubberDuck

的fly()和quack()重写,覆盖父类方法。屏幕看上去舒服多了~

     3.屏幕上怎么又多出来了一个DecoyDuck(诱饵鸭,木头制作),再重写父类方法,让它不会飞也不会叫。

     4.....不一会就感到厌烦了,老是重写了父类方法怎么行。

直接继承有太多的缺点:

  • 代码在多个子类中重复(一直重写父类方法有木有)
  • 运行时行为不容易改变(我的DecoyDuck现在会飞了,能不能在运行的时候改变它)
  • 很难知道所有鸭子的全部行为(总不会把所有子类数一遍吧)
  • 牵一发而动全身

于是,我们要改变设计方法。

这些行为(fly和quack)继承起来太难控制,用接口把。嗯~把它们从DUCK父类中提取出来,分别定义一个接口。

设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混合在一起。


鸭子发出的声音不一样,可以"呱呱Quack"、"吱吱Squack"、"无声MuteQuack",分别实现fly的接口(FlyBehavior)

这样,鸭子发出quack的时候就可以调用特定的方法。

因为分离出来,这些行为和鸭子完全无关。实现青蛙的时候也可以拿走"呱呱叫"的方法

设计原则:针对接口编程,不针对实现编程。

/*
  Dog继承Animal
*/

//针对实现编程
Dog d = new Dog;
d.bark();

//针对接口编程,这里就是多态。
Animal animal = new Dog();
animal.bark();

//用animal调用bark()方法,至于animal是什么,我们并不关心。

那么还有一个问题,每个鸭子都去实现特定的行为方法(如:Quack),这些行为是固定的。可是我想在运行时动态的改变鸭子的行为怎么办?

比如,DecoyDuck突然可以飞了。

貌似可以用多态,针对接口编程。"叫"定义了接口QuackBehavior,"飞"定义了接口FlyBehavior。

然后Quack(呱呱叫),SQuack(吱吱叫),MuteQuack(无声)实现了QuackBehavior。

同理,FlyBehavior也有具体实现。

那么,父类Duck:

public abstract class Duck{

//声明QuackBehavior的接口
QuackBehavior quackBehavior;

//鸭子对象不直接执行叫的行为,委托给quackBehavior对象。
public void performQuack(){
   quackBehavior.quack();
}
//可以动态的修改行为,用quackBehavior来什么行为要什么行为。
//比如传入MuteQuack,然后在调用performQuack,是不是就相当于 MuteQuack.quack(),不发声了。
public void setQuackBehavior(QuackBehavior qb){
quackBehavior = qb;
} }

有一个performQuack()方法,这个方法代替被删除的quack方法(直接继承的quack方法,在最上面)。

作用就是去使用接口对象 quackBehavior调用quack方法。(或许画上类图更清楚)

//叫的接口
public interface QuackBehacior{
   public void quack();
}

------------------------------------具体实现--------------------------------------
public class Quack implements QuackBehavior{
   public void quack(){
   System.out.println("我会呱呱叫");
}
}
----------            ------------
public class Squack implements QuackBehavior{
   public void quack(){
   System.out.println("我会吱吱叫");
}
}
----------            ------------
public class MuteQuack implements QuackBehavior{
   public void quack(){
   System.out.println("我不会发声");
}
}

像不像上面anmial和dog的调用,不需要知道具体是谁,委托给接口对象(可以这样理解吧)。

那么,开始了

写一个鸭子类继承Duck

public MallardDuck extends Duck{

public MallardDuck(){
//quackBehavior是继承下来的
quackBehavior = new Quack();
}

}

开始测试:

public static void main(String [] args){

Duck mallard = new MallardDuck();
mallard.performQuack(); //输出呱呱叫
mallard.setQuackBehavior(new MuteQuack());
mallard.performQuack();//输出无声

}

/*
可能你还有些迷糊,那逐一解释代码
1.Duck mallard = new MallardDuck()
执行:
        quackBehavior = new Quack()//就像实例化了quackBehavior
2.mallard.peiformQuack();
执行:      
        quackBehavior.quack();//该对象调用Quack类里的quack()方法

3.mallard.setQuackBehavior(new MuteQuack());
         quackBehavior = qb; //qb是不是 new MuteQuack();

是不是所有的都是围绕QuackBehavior 接口,针对接口编程。
*/

设计原则:多用组合(composition),少用继承

  上文中的fly(实现方式和quack相同)、quack行为就是组合。鸭子的行为不是继承来的,而是多个行为 组合 来的。

这些行为完全可以被青蛙(呱呱),麻雀(飞)使用,提升了代码的复用程度。

把"行为"抽象一下,不再叫做行为而是算法族。然后把他们封装起来(嗯,封装),可以动态的相互替换(鸭子的行为动态替换)。并且这些算法和要使用这些算法的鸭子没有任何关系。

总结:

 策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

这样看起来是不是很高大上~

转载请注明出处。http://www.cnblogs.com/yuhanghzsd/p/5357981.html

原文地址:https://www.cnblogs.com/yuhanghzsd/p/5357981.html