浅谈 《Head First设计模式》 中的策略模式


引言

算法和数据结构让我在遇到实际问题的时候,能把现实的问题看作抽象结构来解决。

这和OO的想法有些类似,当我们习惯把一切物体都抽象成对象这种我们可以借由语言来解释的东西的时候,

我们可以方便的借用各种数学模型和思想来解决他们所面临的实际需求和问题。

其实在日常工作中,算法知识运用到的少之又少,反而是设计模式在各种项目框架中比比皆是。

策略模式的存在不仅仅是设计框架独有的思想,日程的需求解决方案可以参展设计模式中的思想,

而且在日常协作的过程中,程序员之间用设计模式来交流思路可以极大的方便两人理解和产生共识。

我们尝尝听到的23种设计模式到底是那些,其实设计模式有成千上万,如果真的把他们统一整理下来,

其实可以把他们分为简单的几类而已,根据书中的顺序,整理一下自己对第一种策略模式的理解。

正文

书中第一章运用鸭子的例子来向我们展示OO模型的多用组合,少用继承的思想,我们一步步剖析一下
public abstract class Duck {

    public abstract void display();

    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }

}
这里我们我们看到的就是最简单的一个鸭子的抽象类,这里需要谈一些自己对抽象类的理解
  1.抽象类不能实例化,常作为规范类的存在,所以不能被new
  2.继承抽象类必须重写抽象类中的抽象方法
  3.抽象类中可以存在具体方法,有抽象方法的类不一定是抽象类

所以继承这个抽象类的鸭子类默认都有“游泳的天赋”,而具体其他的“能力”需要自己实现display方法来自定义

现在我们开始丰富这个抽象类,不重新编写一个新类就实现新的“天赋”,
public abstract class Duck {

    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public abstract void display();

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }
}

public interface QuackBehavior {
    public void quack();
}

public interface FlyBehavior {
    public void fly();
}
我们增加了鸭子的叫声和飞行两个接口,并作为参数添加到鸭子类中,而两个接口只有飞和叫两个简单的方法

这样做的目的,是把鸭子的叫和飞两个功能委托给行为类来实现,通过这种组合而不是重新写一个新的鸭子类来实现他

我们来写几个实现类并测试一下继承鸭子类的实现类能否采用行动类里的方法
// 不会飞的鸭子
public class FlyNoWay implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("I can't fly!");
    }
}

// 会飞的鸭子
public class FlyWithWings implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("I am flying!");
    }
}

// 会叫的鸭子
public class Quack implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("Quack");
    }
}

// 安静的鸭子
public class MuteQuack implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("<>");
    }
}

// 实现类
public class MallardDuck extends Duck{
    @Override
    public void display() {
        System.out.println("I am real Mallard duck");
    }

    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
}

// 测试类 
// 运行结果:
// I am flying!
// Quack
public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallardDuck = new MallardDuck();
        mallardDuck.performFly();
        mallardDuck.performQuack();
    }
}
当然这里的行为类并没有完全用到,只是我们在实现类中指定的哪一种鸭子,最后展示的就是那种行为类的鸭子

但是这样虽然实现了组合的灵活性,但是还不够灵活,因为要手动修改鸭子的类型,再次修改以下鸭子类代码
public abstract class Duck {

    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public abstract void display();

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }

    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}
这里我们新增了set方法来指定鸭子的种类,这样做的好处就是鸭子可以动态的变换种类,用心得模板鸭来试一下
// 新的行为类
public class FlyRocketPowered implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("I am flying with a rocket");
    }
}

// 模板鸭
public class ModelDuck extends Duck{
    public ModelDuck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Quack();
    }

    @Override
    public void display() {
        System.out.println("I am a model duck");
    }
}

// 测试类
// 运行结果:
// I can't fly!
// I am flying with a rocket
public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck modelDuck = new ModelDuck();
        modelDuck.performFly();
        modelDuck.setFlyBehavior(new FlyRocketPowered());
        modelDuck.performFly();
    }
}

总结

到这里,我们可以交代一下到底什么是策略模式,定义如下:
  策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户

看定义来说确实不太容易理解,因为设计模式终究是一种思想,没有一种特定的公式让我们套用

其实在自己还没有接触设计模式的时候,有位前辈和我说ifelse或者switch就是策略模式

但是在自己理解后发现,虽然它们运用了策略,但是并没有独立出来,所以并不算是策略模式,只是一种处理方式而已

希望文章可以给大家带来新的想法和思路,如果文章存在问题,欢迎在评论区讨论,各自努力,最高处见