[物語を忘れました]°のブログへようこそ

由一只鸭子引发的模式---策略模式

作者:故事我忘了
个人微信公众号:程序猿的月光宝盒

不算是教程,充其量(本来就)是个笔 ji叭,但也希望不会浪费你的阅读时间~

----------------------------------[Head First 设计模式] 笔记

前置知识:

需要了解java的:

1.封装

2.继承

3.多态

4.抽象

前三也称为java的三大特性

Demo描述:(OO原则解决方式)

一只未知品种的鸭子,众所周知,所有鸭子都会游泳(春江水暖鸭先知),有的鸭子会飞(玩具鸭,绑上飞行器/本来就会飞,方式不同),有的鸭子会叫(玩具鸭也会叫,只是叫声不同)

第一反应其实就是用继承的方法,本来嘛,都是鸭子,都会飞,都会叫,个别的让子类重写不就完了吗

但是,老金要说但是了.假设,我作为小丑鸭子类,我本来就不需要会飞,我也不想飞,为什么你要让我实现对应的飞的功能,要飞天空任你飞,大不了伤痛我来背

这里就引出了第一个设计原则:

一.开闭原则

找出应用中可能需要改变的地方,把他们独立出来,不要和那些不需要变化的混在一起

对修改关闭,对拓展开放

那么在程序中,怎么用设计模式实现这一场景呢?

0. 整体类图

首先我们来看一下整体的UML类图,这个类图是用idea自动生成的,默认快捷键是..我百度一下,嗯 ,还是我的这篇博客 idea快捷键 里面写了

ctrl+shift+alt+u

我们来试一下:

图片

由上可知,有个MallardDuck(绿头鸭) 继承于 鸭子父类,而这个父类里面仅仅是只有对应的行为属性,对应的行为属性也是个接口,具体类图如下:

图片

图片

这样的设计,可以让飞行和呱呱叫的动作被其他对象复用,因为这些行为已经跟鸭子无关了.

而我们能新增一些行为,不会影响到已经有的行为类,也不会影响使用了已经存在的行为的具体鸭子类

这里用到了两个设计原则:

二.依赖倒转原则

依赖于抽象的类,而不要依赖于具体的类;

针对接口或抽象类编程 ,而不是针对具体类编程

三. 组合/聚合复用原则

多用组合,少用继承

好了 我们上代码

1. 具体代码

1.1 关于鸭子

1.1.1 鸭子父类: Duck.java

package firstDuck.duck;

import firstDuck.behavior.IFlyBehavior;
import firstDuck.behavior.IQuackBehavior;

/**
 * 鸭子父类(抽象)
 */
public abstract class Duck {
    /** 飞的行为 定义为接口,在运行时,持有特定行为的引用*/
    protected IFlyBehavior iFlyBehavior;
    /** 叫的行为 也是被定义为接口*/
    protected IQuackBehavior iQuqBehavior;


    public Duck() {
    }

    /**
     * 调用飞的行为去飞
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 23:25
     */
    public void doFly() {
        this.iFlyBehavior.fly();
    }

    /**
     * 调用叫的行为去叫
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 23:25
     */
    public void doQuack() {
        this.iQuqBehavior.quack();
    }

    /**
     * 显示外观 相当于toString
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 22:54
     */
    public abstract void display();

    public void swim() {
        System.out.println("所有的鸭子都会浮在水面上~");
    }
}

1.1.2 鸭子子类(可以有很多这里举例1) MallardDuck.java

package firstDuck.duck.duckSon;

import firstDuck.behavior.impl.FlyWithWings;
import firstDuck.behavior.impl.Quack;
import firstDuck.duck.Duck;

/**
 * 绿头鸭
 *
 * @author 金聖聰
 * @email jinshengcong@163.com
 * @version v1.0
 * @date 2021/01/13 22:55
 */
public class MallardDuck extends Duck {

    /**
     * 绿头鸭构造器
     *
     * @return null
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 23:35
     */
    public MallardDuck() {
        /* 绿头鸭调用Quack类处理正真的呱呱叫 */
        super.iQuqBehavior = new Quack();
        /* 绿头鸭调用FlyWithWings类处理正真的飞行 */
        super.iFlyBehavior = new FlyWithWings();
    }

    public void display() {
        System.out.println("我是一个真正的绿头鸭");
    }
}

1.2 关于行为

1.2.1 飞行的行为 IFlyBehavior.java

这是一个接口,所有飞行类都实现他,所有的飞行类都必须实现fly()方法

package firstDuck.behavior;

/**
 * 飞行的行为
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public interface IFlyBehavior {
    /**
     * 飞行
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 23:11
     */
    void fly();
}
1.2.1.1 飞行行为的实现类 FlyNoWay.java
package firstDuck.behavior.impl;

import firstDuck.behavior.IFlyBehavior;

/**
 * 不能飞
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class FlyNoWay implements IFlyBehavior {
    @Override
    public void fly() {
        System.out.println("我不能飞啊喂~");
    }
}

1.2.1.2 飞行行为的实现类 FlyWithWings.java
package firstDuck.behavior.impl;

import firstDuck.behavior.IFlyBehavior;

/**
 * 翅膀飞
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class FlyWithWings implements IFlyBehavior {
    @Override
    public void fly() {
        System.out.println("用翅膀飞~");
    }
}

1.2.2 叫的行为 IQuackBehavior.java

叫的行为也一样,所有叫的类都实现他,所有的会叫的类都必须实现quack()方法

package firstDuck.behavior;

/**
 * description
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public interface IQuackBehavior {
    /**
     * 叫
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 23:11
     */
    void quack();
}

1.2.2.1 叫的行为的实现类 Quack.java
package firstDuck.behavior.impl;

import firstDuck.behavior.IQuackBehavior;

/**
 * 鸭子嘎嘎叫
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class Quack implements IQuackBehavior {
    @Override
    public void quack() {
        System.out.println("我真鸭子,我嘎嘎叫~");
    }
}

1.2.2.2 叫的行为的实现类 Squeak.java
package firstDuck.behavior.impl;

import firstDuck.behavior.IQuackBehavior;

/**
 * 橡皮鸭子吱吱叫
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class Squeak implements IQuackBehavior {
    @Override
    public void quack() {
        System.out.println("我橡皮鸭子,我吱吱叫~");
    }
}

1.2.2.3 叫的行为的实现类 Squeak.java
package firstDuck.behavior.impl;

import firstDuck.behavior.IQuackBehavior;

/**
 * 哑巴鸭不会叫
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class MuteQuack implements IQuackBehavior {
    @Override
    public void quack() {
        System.out.println("我不会叫~~");
    }
}

1.3 测试类

package firstDuck.test;

import firstDuck.duck.Duck;
import firstDuck.duck.duckSon.MallardDuck;

/**
 * 迷你鸭子模拟器 测试类
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class MiniDuckSimulator {
    public static void main(String[] args) {
        /* 绿头鸭 */
        Duck mallard = new MallardDuck();
        mallard.display();
        mallard.doFly();
        mallard.doQuack();
    }
}

输出:

图片

所以,绿头鸭会真的嘎嘎叫,因为,在构造实例时,构造器会把继承来的iQuqBehavior初始化成Quack类型的新实例,

同样的处理方式也用在飞行行为上

3. 思考一下

在绿头鸭的构造器中,这里是在干啥

public MallardDuck() {
        /* 绿头鸭调用Quack类处理正真的呱呱叫 */
        super.iQuqBehavior = new Quack();
        /* 绿头鸭调用FlyWithWings类处理正真的飞行 */
        super.iFlyBehavior = new FlyWithWings();
    }

根据开头引出的设计原则之依赖倒转原则,不是应该针对接口编程吗,这里怎么又针对实现编程了,你在构造一个具体的叫的行为和飞的行为的实现类啊哥?靠谱一点啊老金~

行,谁说我不靠谱老子锤爆你的头~[doeg保命.gif]

一步一步来吗~

刚开始在鸭子类里建立了一堆动态行为属性(其实就俩)没有用到嘛现在用一下咯

3.1 改造开始:

3.1.1 首先是鸭子类Duck.java
package firstDuck.duck;

import firstDuck.behavior.IFlyBehavior;
import firstDuck.behavior.IQuackBehavior;

/**
 * 鸭子父类(抽象)
 */
public abstract class Duck {
    /** 飞的行为 定义为接口,在运行时,持有特定行为的引用*/
    protected IFlyBehavior iFlyBehavior;
    /** 叫的行为 也是被定义为接口*/
    protected IQuackBehavior iQuqBehavior;

    /**
     * 新增飞行的setter方法
     * @param iFlyBehavior 飞的行为
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/18 23:50
     */
    public void setiFlyBehavior(IFlyBehavior iFlyBehavior) {
        this.iFlyBehavior = iFlyBehavior;
    }

    /**
     * 新增叫的setter方法
     * @param iQuqBehavior 叫的行为
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/18 23:51
     */
    public void setiQuqBehavior(IQuackBehavior iQuqBehavior) {
        this.iQuqBehavior = iQuqBehavior;
    }

    public Duck() {
    }

    /**
     * 调用飞的行为去飞
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 23:25
     */
    public void doFly() {
        this.iFlyBehavior.fly();
    }

    /**
     * 调用叫的行为去叫
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 23:25
     */
    public void doQuack() {
        this.iQuqBehavior.quack();
    }

    /**
     * 显示外观 相当于toString
     *
     * @return void
     * @author 金聖聰
     * @email jinshengcong@163.com
     * @version v1.0
     * @date 2021/01/13 22:54
     */
    public abstract void display();

    public void swim() {
        System.out.println("所有的鸭子都会浮在水面上~");
    }
}

也就是新增两个setter方法,动态设置成员变量

从此以后,我们可以随时动态调用这两个方法改变鸭子的行为,就相当于脑子里植入了芯片,被人为所欲为~啧啧啧

测一下测一下

3.1.2 然后是新建子类 ModelDuck.java
package firstDuck.duck.duckSon;

import firstDuck.behavior.impl.FlyNoWay;
import firstDuck.behavior.impl.MuteQuack;
import firstDuck.duck.Duck;

/**
 * description
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class ModelDuck extends Duck {
    public ModelDuck(){
        /* 一开始不会飞  */
        super.iFlyBehavior = new FlyNoWay();
        /* 一开始不会叫  */
        super.iQuqBehavior = new MuteQuack();
    }

    @Override
    public void display() {
        System.out.println("我是模型鸭子");
    }
}

3.1.3 新建一个飞行行为让火箭飞! FlyByRocket.java
package firstDuck.behavior.impl;

import firstDuck.behavior.IFlyBehavior;

/**
 * description
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class FlyByRocket implements IFlyBehavior {
    @Override
    public void fly() {
        System.out.println("插了火箭了,上天,冲干饭鸭~");
    }
}

3.1.4 修改测试类
package firstDuck.test;

import firstDuck.behavior.impl.FlyByRocket;
import firstDuck.duck.Duck;
import firstDuck.duck.duckSon.MallardDuck;
import firstDuck.duck.duckSon.ModelDuck;

/**
 * 迷你鸭子模拟器 测试类
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 */
public class MiniDuckSimulator {
    public static void main(String[] args) {
        /* 绿头鸭 */
        Duck mallard = new MallardDuck();
        mallard.display();
        mallard.doFly();
        mallard.doQuack();
        System.out.println();
        System.out.println("-----------改造后------------");
        Duck modelDuck = new ModelDuck();
        modelDuck.display();
        modelDuck.doQuack();
        modelDuck.doFly();
        /* 一开始是不会飞的,直到我给他插了火箭,摇身一变冲天干饭鸭  */
        modelDuck.setiFlyBehavior(new FlyByRocket());
        /* 现在插了火箭了,看看阿能飞了 */
        modelDuck.doFly();
    }
}

运行结果:

图片

好了,恭喜~get策略模式

那最终官方的策略模式定义是什么呢?背下来,适合装逼~

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

怎么背啊,淦?

算法族想像成刚才的飞行方式,有这么多飞行方式需要用,是不是定义了一个接口用来封装?然后具体用的时候只要new具体的行为方式到setter方法里就能动态改变了?具体的模型鸭子还是不会飞吧,插火箭这个动作是你插的吧,你能插,也能拔吧?

我没有开车,get out~

所以上边的助记就是

定义了算法族(行为方式),分别封装起来(做成接口),让他们之间可以互相替换(动态set调用),此模式让算法(行为)的变化独立于使用算法(行为)的客户

总结一下:

设计原则:

1.开闭原则

对修改关闭,对拓展开放,找出程序中变化的部分,封装独立出来

2.依赖倒转

依赖于抽象,而不依赖于具体;

针对接口/抽象编程,不针对具体类编程

3.组合复用

多用组合,少用继承

4.策略模式定义

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

posted @ 2021-01-19 00:43  故事我忘了°  阅读(173)  评论(0编辑  收藏  举报