设计模式之策略模式

设计模式之策略模式

        这是本人第一篇技术博客,写的不好的地方请多多指正,大家一起进步.在看了Head First之后,决定给自己写一个读书笔记,用来总结归纳自己学到了什么,并把它用自己的语言总结出来,一方面方便自己回顾,另一方面,自己总结一下自己领悟到的东西.

第一阶段

        首先从鸭子这个抽象类开始,给鸭子赋予了三种方法:quack(),swim()和抽象方法display().所有的鸭子都得继承这个抽象类,这样做并没有任何问题.代码如下

package com.yczuoxin.pattern;

public abstract class Duck {
    public void quack(){
        System.out.println("呱呱叫");
    }
    public void swim(){
        System.out.println("会游泳");
    }
    public abstract void display();
}

要创建一个类型的鸭子,只需要继承这个Duck类就可以了,例如现在创建一个绿头鸭:

package com.yczuoxin.pattern;

public class MallardDuck extends Duck {
    @Override
    public void display() {
        System.out.println("绿头");
    }
}

此时我们创建的绿头鸭已经可以呱呱叫,游泳以及拥有了绿头的独特属性:

package com.yczuoxin.pattern;

public class Test {
    public static void main(String[] args) {
        MallardDuck mallardDuck = new MallardDuck();
        mallardDuck.quack();
        mallardDuck.swim();
        mallardDuck.display();
    }
}
呱呱叫
会游泳
绿头

第二阶段

        此时需要增加需求,让部分鸭子会飞,此时如果简单地在Duck类中增加一个fly()方法的话就会导致所有的鸭子都会飞了:

package com.yczuoxin.pattern;

public abstract class Duck {
    public void quack(){
        System.out.println("呱呱叫");
    }
    public void swim(){
        System.out.println("会游泳");
    }
    public void fly(){
        System.out.println("在天上飞");
    }
    public abstract void display();
}

此时如果不对继承Duck的类(例如RubberDuck,与MallardDuck一致)做任何的操作,那么就会出现:

package com.yczuoxin.pattern;

public class Test {
    public static void main(String[] args) {
        RubberDuck rubberDuck = new RubberDuck();
        rubberDuck.quack();
        rubberDuck.swim();
        rubberDuck.display();
        rubberDuck.fly();
    }
}
呱呱叫
会游泳
我是一个橡皮鸭
在天上飞

橡皮鸭都可以在天上飞了,这明显不符合常理.但此时有一种方法可以缓解这个问题,就是重写fly()方法,并且不作任何操作:

package com.yczuoxin.pattern;

public class RubberDuck extends Duck {
    @Override
    public void display() {
        System.out.println("我是一个橡皮鸭");
    }
    @Override
    public void fly() {
        // 不作任何操作
    }
}

此时输出的结果发现,橡皮鸭在天上飞的方法没有做任何操作,但是按照逻辑,鸭子不应该都拥有fly()的行为.所以不应该让Duck拥有fly()方法,删掉该方法,于是用实现接口的方式,让那些需要有飞的能力的鸭子实现flyable接口:

package com.yczuoxin.pattern;

public interface Flyable {
    void fly();
}

此时绿头鸭实现Flyable()接口拥有了飞的能力

package com.yczuoxin.pattern;

public class MallardDuck extends Duck implements Flyable {
    @Override
    public void display() {
        System.out.println("绿头");
    }
    @Override
    public void fly() {
        System.out.println("在天上飞");
    }
}

而橡皮鸭不实现Flyable()接口,也就不会有fly()方法,即没有飞的能力了.

小结继承的一些缺点:

        1.代码在多个子类中重复.

        2.运行时的行为不易被改变.

        3.很难知道超类(父类)的全部方法.

        4.改变会牵一发而动全身,造成其他子类不想要的改变.

第三阶段

        虽然我们使用接口解决了所有鸭子都会飞的缺陷,但是代码无法复用又成了接口造成的问题,比如鸭子有什么样的飞行动作,相同的动作每次都要写一遍,并且导致你无论何时需要修改某个行为,你必须的往下追踪并在每一个定义此行为的类中修改它,一不小心就可能造成新的错误.此处引出了第一个设计原则——找出可能需要改变的部分,把他们独立出来,与不变的代码区分.此时把鸭子的飞属性单独建立一个行为类,索性,我们把多态也融入进来,使得鸭子的飞行方法可以动态的改变.为了实现这个功能,引入了第二个设计原则——面向接口编程.比如我们设定一个飞行为的接口FlyBehavior,然后对于不同的飞行动作创建实现类去实现FlyBehavior接口.

package com.yczuoxin.pattern;

public interface FlyBehavior {
    void fly();
}

然后一个飞的实现类和一个不会飞的实现类:

package com.yczuoxin.pattern;

public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("在天上飞");
    }
}
package com.yczuoxin.pattern;

public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("我不会飞");
    }
}

package com.yczuoxin.pattern;

public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        // 不作任何操作
System.out.println("在天上飞");
}}

此时修改Duck类使得飞成为鸭子的一个属性,并将

package com.yczuoxin.pattern;

public abstract class Duck {
    
    FlyBehavior flyBehavior;
    
    public void quack(){
        System.out.println("呱呱叫");
    }
    public void swim(){
        System.out.println("会游泳");
    } 
    public void performFly(){
        flyBehavior.fly();
    }
    public abstract void display();
}

在实例化特定的具体行为在实现了FlyBehavior的类中:

package com.yczuoxin.pattern;

public class MallardDuck extends Duck{

    public MallardDuck(){
        flyBehavior = new FlyWithWings();
    }
    @Override
    public void display() {
        System.out.println("绿头");
    }
}
package com.yczuoxin.pattern;

public class RubberDuck extends Duck {

    public RubberDuck(){
        flyBehavior = new FlyNoWay();
    }
    @Override
    public void display() {
        System.out.println("我是一个橡皮鸭");
    }
}

此时在看看测试类的结果

package com.yczuoxin.pattern;

public class Test {
    public static void main(String[] args) {
        Duck rubberDuck = new RubberDuck();
        rubberDuck.quack();
        rubberDuck.swim();
        rubberDuck.display();
        rubberDuck.performFly();
    }
}
呱呱叫
会游泳
我是一个橡皮鸭
我不会飞

package com.yczuoxin.pattern;

public class Test {
    public static void main(String[] args) {
        Duck mallardDuck = new MallardDuck();
        mallardDuck.quack();
        mallardDuck.swim();
        mallardDuck.display();
        mallardDuck.performFly();
    }
}
呱呱叫
会游泳
绿头
在天上飞

由此一来,我们利用继承和实现接口的方式及解决了继承带来的包袱,也解决了实现接口导致的代码无法复用.并且将飞行的动作交给了别的类去处理.然后利用了多态使得飞行的行为在运行时可以动态的去改变.

第四阶段

        此时我们其实并没有实际上的在运行时动态,而是在实例的构造函数中来设定子类的行为,此时我们应该用更好的方法实现真正意义上的动态,就是使用setter方法.

package com.yczuoxin.pattern;

public abstract class Duck {

    FlyBehavior flyBehavior;

    public void quack(){
        System.out.println("呱呱叫");
    }
    public void swim(){
        System.out.println("会游泳");
    }
    public void performFly(){
        flyBehavior.fly();
    }
    public abstract void display();

    public FlyBehavior getFlyBehavior() {
        return flyBehavior;
    }

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

而此时,我们子类不再需要构造函数去设定行为的实现类.只是在实例化的时候记得set进实例就好了,此处就放一个

package com.yczuoxin.pattern;

public class Test {
    public static void main(String[] args) {
        Duck rubberDuck = new RubberDuck();
        rubberDuck.quack();
        rubberDuck.swim();
        rubberDuck.display();
        rubberDuck.performFly();
        rubberDuck.setFlyBehavior(new FlyWithWings());
        rubberDuck.performFly();
    }
}
呱呱叫
会游泳
我是一个橡皮鸭
我不会飞
在天上飞

此处可以看出,我们实现了动态的使不会飞橡皮鸭即刻拥有了飞翔的能力,这也就是是说明实现了动态给实例赋予了不同的行为.这便是策略模式.我总结的UML图如下:


个人小结

        从Duck类看来,将飞这类方法写成了一个属性,表示鸭子会做的事情,也可以引申出为一个对象拥有的一系列为了解决一个行为的方法的接口.把策略抽象出来,包装行为和算法,在使用的时候我们可以根据不同的场景,选择自己需要的行为或者算法来满足,从而减少if..else的书写,使得代码维护起来更加方便,但是缺点就是每一个方法或者算法都要写一个类,这样就会使得类变得特别多.




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值