1. 策略模式.md

策略模式

定义:

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

概念

  • 超类
  • 抽象
  • 封装
  • 多态
  • 继承

关键字

  1. 组合(在这里是多态的体现,组合的内容是接口或者抽象类)
  2. 抽象分离行为(动作)
  3. 算法族

OO(设计)原则

1. 封装变化

书中P9的原话如下:

找出应用中可能需要变化之处,把他们独立出来,不用和那些不需要变化的代码混在一起

当然书中也提供了另外一种思考方式(也是在第九页的下方)

把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部门,而不影响不需要变化地其他部门

转换的思想:
把会变化的部分取出并“封装”起来,好让其他部门不会受到影响。

这样整的结果:
代码变化引起的不经意后果变少,系统变得更有弹性。

将会下方中讲解如何用到这个原则

2. 针对接口编程,不针对实现

书中原话:P11

首先得懂得“接口”这个概念:

接口:在java中主要是用于定义“行为”(也就是动作),然后让子类实现该接口时,实现该方法。

另外,接口的另外一种作用:是多态。可以在“运行时”动态地“改变”。(引入一个问题:说明是“运行时”,我的理解是:在程序跑的过程中,可以进行调用不同的该接口的实例去切换 – 例如:一个游戏中,猎人可以切换不同的武器进行攻击。那么武器就是一个接口,斧头以及匕首都是对应的实现类,在程序的运行的过程中,可以进行在不同的场景切换不同的武器【书中P34页】)

多态: 程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为,不会被绑死在超类型的行为上。

在P12页中有更详细对“针对接口编程”的说明:
针对接口编程 真正意思是:“针对超类型(supertype)编程”【抽象超类型可以是抽象类或者接口】

针对超类型编程 == “变量的声明类型应该是超类型(Animal a) ,通常是一个抽象类或者是一个接口,如此,主要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这也意味着,声明类时不用理会以后执行时的真正对象类型”

3. 多用组合,少用继承

组合前提要懂得一个概念: “有一个”。
例如: 一个鸭子里面“有一个”行为:飞行行为类,那么可以将飞行行为类和鸭子类进行组合在一起。
组合的好处:使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以“在运行时动态地改变行为”,只要组合地行为对象符合正确地接口标准即可。

概念以及原则讲完后,那么就从书中copy例子了。

例子:

1.简单地模拟鸭子

有一个鸭子,会叫、会游泳,有展示自己地行为。

1.1 使用标准地OO技术,设置一个鸭子的超类:
1. 策略模式.md
1.2 突然新增一个需求,鸭子有飞行的行为:
1. 策略模式.md

1.3 可是新增一个“橡皮鸭子”继承了鸭子超类,突然有一个问题了:橡皮鸭子怎么飞呢?
1. 策略模式.md
1.4 于是乎,从继承的角度来讲,鸭子超类中的fly()方法设置为抽象方法,让所有的子类都重新覆盖fly()方法。

至此,引出的一个问题:
绿头鸭、红头鸭都会飞,可让他们重新覆盖这些方法的话,重复有冗余。

2.抽象行为并实现 – 利用接口实现

2.1 既然fly()是抽象方法,那为何不定义说哪个鸭子需要实现飞行fly方法的话,那就让他去实现算了。
于是乎得到如下图片:
1. 策略模式.md

2.1的方式虽然解决部分实例问题(橡皮鸭),但是会造成代码无法复用了(举个栗子:如果有48个不同类型的Duck,那么每个Duck都需要重新覆盖fly方法。。。太多了,并且后期假如每一个鸭子都需要修改fly里面的具体实现的话,,,会奔溃的)

3.设计原则

回顾第一点和第二点,是否觉得问题好麻烦,这也不对,那也不对的。
那么,把所有东西都不管着先 – 把问题归零

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

上述的内容中,整理出如下内容:
不会变化的部分:

  • Duck类中的swin()方法和抽象方法display() 方法是不会变化的

变化的部分:

  • fly() 和 quack() 方法是会根据不同的类型的鸭子而有不同的行为。

为了要把两个行为从Duck类中分开,建立一组新类来代表每个行为。

到此,下一步工作是如何设计鸭子的行为。 提示: 行为 是动作,那么与之挂钩的应该是接口

3.2 设计原则:针对接口编程,而不是针对实现编程

如何设计实现飞行和呱呱叫的行为的类呢?-- 在一切能有弹性的基础上去设计实现。

如果在鸭子类中包含设定行为的方法,那么-》在“运行时”动态的“改变”绿头鸭的飞行行为。(这里其实包含了另一种的设计原则:组合 – 什么是组合,在这里我的定义是:在主要类(Duck)中的接口(FlyBehavior、QuackBehavior)或者抽象类的实例变量,他们组合在主要的类中,然后如果独立出来便没有什么意义,他们是主要类中的一部分,这便是组合。组合并不是什么实体,而是一种关系,值得是主体以及被分离出来的部分(可从uml中学习)的一种关系)。

结合3.1和3.2 得出如下的UML图
1. 策略模式.md

3.3 设计原则: 多用组合,少用继承

组合: 在uml中,组合就是“有一个”(废话:有一个就是组合咯)。组合从字面上的定义上来说:组合成主类的一部分,被分出来后,没有什么意义了。

于是乎:得出以下的uml:
1. 策略模式.md

4.代码实现

代码架构:

1. 策略模式.md

4.1 行为接口

FlyBehavior.java

package com.behavior;
public interface FlyBehavior {
    public void fly();
}

QuackBehavior.java

package com.behavior;
public interface QuackBehavior {
    void quack();
}

4.2 接口实现类
4.2.1 FlyBehavior的接口实现类

FlyWithWings.java

package com.behavior.impl;
import com.behavior.FlyBehavior;
public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("用翅膀飞");
    }
}

FlyRocketPowered.java

package com.behavior.impl;
import com.behavior.FlyBehavior;
public class FlyRocketPowered implements FlyBehavior {
    public void fly() {
        System.out.println("我是用火箭飞的");
    }
}

FlyNoWay.java

package com.behavior.impl;
import com.behavior.FlyBehavior;
public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("这是不能飞");
    }
}

4.2.2 QuackBehavior的接口实现类

Quack.java

package com.behavior.impl;
import com.behavior.QuackBehavior;
public class Quack implements QuackBehavior {
    public void quack() {
        System.out.println("呱呱叫");
    }
}

Squeak.java

package com.behavior.impl;
import com.behavior.QuackBehavior;
public class Squeak implements QuackBehavior {
    public void quack() {
        System.out.println("叽叽叫");
    }
}

MuteQuack.java

package com.behavior.impl;
import com.behavior.QuackBehavior;
public class MuteQuack implements QuackBehavior {
    public void quack() {
        System.out.println("不会叫");
    }
}

4.3 Duck抽象类以及它的子类

Duck.java

package com.entity;
import com.behavior.FlyBehavior;
import com.behavior.QuackBehavior;
/**
 * 鸭子类,为抽象类
 */
public abstract class Duck {
    QuackBehavior quackBehavior;
    FlyBehavior flyBehavior;
    public void swim(){
        System.out.println("I can swin");
    }
    // 展示样貌,子类必须实现它
    public abstract void display();
    // 父类调用组合的方法
    public void  performQuack(){
        quackBehavior.quack();
    }
    // 父类中调用组合的方法 -- 利用多态,调用运行时所赋值的真正实例
    public void performFly() {
        flyBehavior.fly();
    }
    // 运行时可调用
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
    // 运行时可调用
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}

MallardDuck.java

package com.entity;
import com.behavior.impl.FlyWithWings;
import com.behavior.impl.Quack;
/**
 * 绿头鸭
 */
public class MallardDuck extends Duck{
    // 这里还是不够灵活的--弹性不足的情况
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    public void display() {
        System.out.println("这是绿头鸭");
    }
}

ModelDuck.java

package com.entity;
import com.behavior.impl.FlyNoWay;
import com.behavior.impl.Quack;
public class ModelDuck extends Duck {
    public ModelDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyNoWay();
    }
    public void display() {
        System.out.println("绿头鸭");
    }
}

4.4 调用

package com.cxx;
import com.behavior.impl.FlyNoWay;
import com.behavior.impl.FlyRocketPowered;
import com.behavior.impl.Squeak;
import com.entity.Duck;
import com.entity.MallardDuck;
import com.entity.ModelDuck;
public class strategypattern {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        mallard.display();
        mallard.performFly();
        mallard.performQuack();
        System.out.println("---------------------");
        // 运行时改变行为
        mallard.setFlyBehavior(new FlyNoWay());
        mallard.setQuackBehavior(new Squeak());
        mallard.performFly();
        mallard.performQuack();

        System.out.println("======");
        Duck modelDuck = new ModelDuck();
        modelDuck.display();
        modelDuck.performFly();
        // 运行时改变为用火箭飞
        modelDuck.setFlyBehavior(new FlyRocketPowered());
        modelDuck.performFly();
    }
}

5.输出

这是绿头鸭
用翅膀飞
呱呱叫
---------------------
这是不能飞
叽叽叫
======
绿头鸭
这是不能飞
我是用火箭飞的

5.总结

1. 策略模式.mdg)

如何使用该模式呢?
第一点:抽象分离出行为,行为有“算法族”的时候,并且能够“组合”在主类中。
关键字:主类、行为、算法族、组合、多态