策略模式

策略模式

这个是第一次写设计模式博客的第一个模式,所以内容也会相对多一些。这些内容不光是为了做一个笔记,也是希望能帮助一些正在学习设计模式的同行(虽然知道也没什么人会看,不过不用在意这些)

此篇及以后所写的设计模式都是基于Head First设计模式为蓝本,里面会涉及一些本书内容,但并不想将近全部内容原封不动地照搬上来(如喜欢本书请支持购买正版)

看完本书第一个设计模式,作者也算是用心,花了很大篇幅介绍了一些OO编程原则。这里也想记录一下,跟读者分享一下。无论是不是有点过时,对我们后面的学习都是有一定的帮助。

对于这个策略模式,如果了解Java 8新特性的人可能觉得这个模式在逐渐淡出舞台。是的,我们不得不承认没有什么东西是一层不变的,技术也是不断运动发展的。就好像Java 8之前interface不能写实体方法,Java 8之后可以写了一样(以后面试提到关于interface的问题要小心了,不要在一位地背宝典,一定要多方面地去了解。可能随着技术的日新月异,这些问题也不会再问了吧)

要点

  • 良好的OO设计必须具备可复用可扩充可维护三个特性。(说白了就是如果你的项目只要一修改就要牵一发而动全身的话,那说明整体架构设计的不是很好)
  • 模式不是代码,而是针对设计问题的通用解决方案。你可把它们应用到特定的应用中。
  • 大多数的模式都允许系统局部改变独立于其他部分。

OO原则

  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不要针对实现编程

说了那么多正式进入主题了。问题来了,究竟什么是策略模式?笔者认为,策略模式的精髓就是:用接口解耦而避免用继承重写。可能很多人对类或者封装的理解是: 对于一个实物的抽象。说通俗点就是一个类包含描述这个实体的状态字段以及行为方法。然而在策略模式里有点打破这种思维,策略模式是把变化的行为方法独立出来封装进接口里,通过接口的多态解耦合。如果只是一位地继承父类重写父类方法,会造成很多重复的代码片段并且难以维护。这里多提一点,对于什么时候使用继承这种问题,笔者认为没有准确的答案,不过根据个人经验来看,继承是一种高度统一的思想(类似于农民家庭世世代代都继承家里的土地播种,觉得手里有地就是福,这里不讨论那些发愤图强改变命运的个例),在一定范围内没有太大变化的情况下使用的。下面我就来具体看看这个策略模式。

这里也是借用一下本书的duck例子。

父类duck

public class Duck {

    public void quack () {
        // 方法实现
    }

    public void fly() {
       // 方法实现
    }
}

非常easy。如果我们按照以往惯例,对于那些实实在在的对象,我们可能会选择继承这个父类,例如

public class MallarDuck extends Duck {

    @Override
    public void quack() {
        System.out.println("mallar duck quack");
    }

    @Override
    public void fly() {
        System.out.println("mallar duck fly");
    }
}

如果一般鸭子都有“叫声”,都会"飞"的话,那一两个实例用继承问题也不大。但是我们要长远考虑,如果后期会有各种各样的鸭子要实现,每次都继承父类重写这些方法那不是太费劲了。而且有些鸭子比较特别,不一定会飞但会叫,不会叫但会飞等等。假如我们规定我们实现的鸭子都不能叫了,那不是所有的鸭子实现类都要进行修改。。。。。这一系列的问题都促使我们不能一味地选择继承。很明显,这里的quack和fly行为动作是根据具体业务情况不断变化的,也就是说我们要根据情况能够“动态”地改变他们。那我们应该怎么去解决呢。

这里我们要把这些变化的行为抽出来,让接口来“组合”(参考上面说的OO原则)

先定义两个行为接口 

public interface FlyBehavior {

    void fly();
}
public interface QuackBehavior {

    void quack();
}

根据不同的需求,创建他们的实现行为类。这里就列举一个。

public class FlyNoWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("cannot fly");
    }
}

当然笔者写了几个实现,这里就不一一贴出来了。

最后我们看一下鸭子是怎么“动态”改变行为的

一样先看父类

public class Duck {
    protected QuackBehavior quackBehavior;

    protected FlyBehavior flyBehavior;

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

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

    public void display() {
        System.out.println("I'm a duck");
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

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

这里稍微丰富了一下,比之前的多了很多。我们看到这个duck父类多了两个行为接口,就是我们上面定义的。

public class MallarDuck extends Duck {

    public MallarDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyNoWings();
    }

    public void display() {
        System.out.println("I'm a real mallar duck");
    }
}

实现类里面,我们在构造器里去实例化鸭子的行为。

最后我们启动程序看一下

public class Entrance {

    public static void main(String[] args) {

        Duck mallarDuck = new MallarDuck();
        mallarDuck.display();
        mallarDuck.performFly();

        // dynamically change fly behavior
        mallarDuck.setFlyBehavior(new FlyRocketPowered());
        mallarDuck.performFly();
    }
}

在之前的例子中,父类duck实现了quack和fly行为类的set方法,当我们需要改变鸭子行为时,只要调用鸭子行为set方法就可以改变。同时,每个鸭子实现类都有不同的实现内容,我们只需实现那些行为接口就好。例如,鸭子a,鸭子b和鸭子c需要a1 a2 b1 b2方法,那我们只需实现a1 a2 b1 b2方法类,然后把这些方法实现类传入进鸭子a b c里(或在构造器里初始化),哪天要修改了就动态地调用一下set方法,就不用每次实现一个鸭子都要去实现一遍a b方法。是不是觉得这样要比继承的可扩展性好很多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值