设计模式之 策略模式

策略模式

  • 由场景推演策略模式

  • 业务场景:

    1. 有一个游戏公司,制作一款鸭子游戏, 在这个鸭子游戏中,角色都是鸭子,不同的鸭子之间,有共性,所有为了提高代码重用性
    2. 开发人员,就制作了一个鸭子的父类中: Duck,把这些鸭子的共性提到父类中
  • 此时在开发的初期阶段,并且负责编码的是同一个人

//鸭子类
abstract class Duck {

    //叫
    public void quack() {
        System.out.println("嘎嘎");
    }

    //游泳
    public void swim() {
        System.out.println("游泳");
    }


    //外观...鸭子外观都不一样
    public abstract void display();
}

//绿头鸭 继承 鸭子
class MallardDuck extends Duck {

    @Override
    public void display() {
        System.out.println("绿头鸭");
    }
}

//红头鸭 继承 鸭子
class RedHeadDuck extends Duck {

    @Override
    public void display() {
        System.out.println("红头鸭");
    }
}

public class AppTest {

    public static void main(String[] args) {
        Duck d = new RedHeadDuck();
        d.quack();
        d.swim();
        d.display();
    }
}

设计模式之 策略模式_第1张图片

  • 就目前而言, 没有什么问题, 请看下面的的场景

  • 业务场景:

    1. 游戏的公司的老总们开会,得出一个提高本公司游戏竞争力的方法: 要求让游戏中的鸭子能飞起来! 把其它竞争者远远甩在身后!
  • 解决方案:

    1. 程序猿就会想, 是时候展示我们面向对象程序猿的威力了!, 我们只需要在父类Duck中添加一个fly方法, 那么所有的Duck的子类都具有fly方法
  • 问题:

    1. 此时, 问题看似解决了,但实际上出现了更麻烦的问题, 所有Duck的子类鸭子,统统都会飞了!
    2. 要知道,父类中的方法,并不是所有子类都能通用的!! 比如: 橡皮鸭 橡皮鸭是没有生命的,不能飞!
    3. 结果因为继承了Duck, 搞得橡皮鸭也能飞了!这样程序猿就会背锅,被老总批评…
  • 代码:

//鸭子类
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();
}

//绿头鸭
class MallardDuck extends Duck {

    @Override
    public void display() {
        System.out.println("绿头鸭");
    }
}

//红头鸭
class RedHeadDuck extends Duck {

    @Override
    public void display() {
        System.out.println("红头鸭");
    }
}

//橡皮鸭
class RubberDuck extends Duck {

    //因为橡皮鸭不会想真实的鸭子一样叫,所有模拟了一下橡皮鸭特有的叫声!
    //所有重写这个方法
    @Override
    public void quack() {
        System.out.println("吱吱叫...");
    }

    @Override
    public void display() {
        System.out.println("外观是橡皮鸭");
    }

    //因为橡皮鸭不会飞,缺又继承了Duck中的fly方法,所有我们可以像重写quack方法那样,去重写fly方法
    @Override
    public void fly() {
        System.out.println("你行你来...(橡皮鸭最终没有飞起来)");
    }
}

设计模式之 策略模式_第2张图片

  • 看起来,问题好像解决了,但是并没有!
  • 只要变化不断,问题就会持续出现
    1. 一会加个木头鸭子,一会加个鸭子超人,一会加个怪鸭伯爵,程序猿就要在每次添加新的鸭子角色时,都要判断新的鸭子角色会不会叫,会不会飞
    2. 针对不同鸭子,要有不同的处理方法,这样也是麻烦,只不过是从一个噩梦,跳入另一个噩梦

  • 针对变化不断,程序猿要判断每个鸭子子类,谁会不会叫,谁会不会飞,就重写quack方法,不会飞就重写fly方法.
    这个工程量很大的!
  • 我们希望那些不会飞的鸭子,压根就没有fly方法,不会叫的方法,压根就没有quack方法
  • 解决方法:
    1. 把这两个经常在子类中变化的方法,从父类中分出来,分成两个接口: fly , quack
//可以飞行接口
interface Flyable {
    void fly();
}

//叫接口
interface Quackable {
    void quack();
}

//鸭子类
abstract class Duck {

    //游泳
    public void swim() {
        System.out.println("游泳");
    }


    //外观...鸭子外观都不一样
    public abstract void display();
}

//绿头鸭 继承 鸭子类, 实现 飞 和 叫 接口,然后进行重写
class MallardDuck extends Duck implements Flyable,Quackable {

    @Override
    public void display() {
        System.out.println("绿头鸭");
    }

    @Override
    public void fly() {
        System.out.println("嘎嘎叫");
    }

    @Override
    public void quack() {
        System.out.println("我飞");
    }
}

//红头鸭 继承 鸭子类, 实现 飞 和 叫 接口,然后进行重写
class RedHeadDuck extends Duck implements Flyable,Quackable {

    @Override
    public void display() {
        System.out.println("红头鸭");
    }

    @Override
    public void fly() {
        System.out.println("嘎嘎叫");
    }

    @Override
    public void quack() {
        System.out.println("我飞");
    }
}

//橡皮鸭 继承 鸭子类, 不会飞,所有只实现叫接口
class RubberDuck extends Duck implements Quackable {

    @Override
    public void display() {
        System.out.println("外观是橡皮鸭");
    }

    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }

}

//诱饵鸭 不会飞 也 不会叫 所有只继承鸭子
class DecoyDuck extends Duck  {

    @Override
    public void display() {
        System.out.println("外观是诱饵鸭");
    }
}

设计模式之 策略模式_第3张图片

  • 思考: 这样问题解决了吗? 没有!

  • 缺点1:

    • 以前:
      • 是每加入一个新的鸭子角色,程序猿就要判断,这个新鸭子角色是否会飞,是否会叫,不会飞就重写飞方法,不会叫就重写叫方法.
    • 现在:
      • 是每加入一个新的鸭子角色,程序猿就要判断,这个新鸭子角色是否会飞,是否会叫,不会飞就不是想Flyable飞方法,不会叫就不实现Quackable.
  • 如此,程序猿仍然没有减少工程量啊?仍然要不断判断新鸭子角色

  • 缺点2:

    • fly, quack 方法没有重用性可言,比如48种鸭子,那么飞方法就要在40个鸭子子类中一共重复40次!
  • 杠点:

    • 从jdk1.8开始,接口中的方法就有默认实现! 此时48种鸭子,那么飞方法只需要在Flyable定义一个默认实现即可,只写1次.
例如: 飞接口的默认实现
//飞接口
interface Flyable {
    default void fly() {
        System.out.println("我飞");
    }
}
  • 解释:
    • 对于48中鸭子,有12种飞行方法,此时又该如何?? 飞行方法,仍然在子类中重复

  • 针对上面的问题,修改代码如下
    • 把飞行和叫方法,从鸭子类中分离出来了!(注意这里的分离,与上面把接口单纯分离不一样)
//飞行行为接口
interface FlyBehavior {
    public void fly();
}

//第一种飞行方式
class FlyWithWings implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("用翅膀飞~~~");
    }
}

//第二种飞行方式
class FlyWitRocket implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("背上绑个窜天猴!!!");
    }
}

//第三种飞行方式
class FlyWithKic implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("被人一脚踢飞!!!");
    }
}

//第四种飞行方式
class FlyNoWay implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("飞不起来!!");
    }
}


//叫的方式接口
interface QuackBehavior {
    public void quack();
}

//第一种
class Quack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("嘎嘎叫");
    }
}

//第二种
class Squeak implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }
}

//第三种
class MuteQuack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("<<静默的>>");
    }
}


//鸭子类
abstract class Duck {

    protected FlyBehavior fb; //飞行方式的接口
    protected QuackBehavior qb;	//叫方式的接口

    //游泳
    public void swim() {
        System.out.println("游泳");
    }

	//飞
    public void performFly() {
        fb.fly();
    }

	//叫
    public void performQuack() {
        qb.quack();
    }


    //外观...鸭子外观都不一样
    public abstract void display();
}

//绿头鸭 继承 鸭子接口
class MallardDuck extends Duck{

	//构造函数
    public MallardDuck() {
        this.fb = new FlyWithWings(); //飞行的方式
        this.qb = new Quack();	//叫的方式
    }

    @Override
    public void display() {
        System.out.println("绿头鸭");
    }

}

//红头鸭 继承 鸭子接口
class RedHeadDuck extends Duck{

    public RedHeadDuck() {
        this.fb = new FlyWithWings();  //飞行的方式
        this.qb = new Quack(); //叫的方式
    }

    @Override
    public void display() {
        System.out.println("红头鸭");
    }

}

//橡皮鸭
class RubberDuck extends Duck{

    public RubberDuck() {
        this.fb = new FlyNoWay();   //飞行的方式
        this.qb = new Squeak(); //叫的方式
    }

    @Override
    public void display() {
        System.out.println("外观是橡皮鸭");
    }
}

//诱饵鸭
class DecoyDuck extends Duck {

    public DecoyDuck() {
        this.fb = new FlyNoWay(); //飞行的方式
        this.qb = new MuteQuack();   //叫的方式
    }

    @Override
    public void display() {
        System.out.println("外观是诱饵鸭");
    }
}


public class AppTest {

    public static void main(String[] args) {
        RedHeadDuck d = new RedHeadDuck();

        d.performFly();
        d.performQuack();
        d.swim();
        d.display();
    }
}

设计模式之 策略模式_第4张图片
设计模式之 策略模式_第5张图片
设计模式之 策略模式_第6张图片

  • 此时,针对于48种鸭子,有12种飞行方法而言,每种飞行方法,写一次!
  • 这个就是传说中的策略模式
  • 疑问1:
    1. 这样不是每次也要自己写new对象吗?和以前实现接口有什么区别
  • 答1:
    1. 这里只是举例子所有飞或者叫才用一句话来显示,真实的代码可能几百行…
    2. 所有相比较的话,写几百行和写一个new谁的工作量大就可想而知了

  • 不仅如此我们还可以获取protected FlyBehavior fb;protected QuackBehavior qb;的get和set方法
  • 然后来改变飞行的方式
public class AppTest {

    public static void main(String[] args) {
        RubberDuck d = new RubberDuck();

        d.display();
        d.performQuack();
        d.swim();
        d.performFly();
        d.setFb(new FlyWithKic()); //还能改变飞行模式
        d.performFly();

    }
}

设计模式之 策略模式_第7张图片

  • 不仅如此,也可以在别人接受我们代码的时候,但需要扩展的时候,直接实现一个接口,然后写一次实现,那么就可以让所有需要这个功能的鸭子,全部用这个方法
  • 例如:
class FlyWitchCat implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("头上顶个竹蜻蜓飞");
    }
}

你可能感兴趣的:(#,设计模式,设计模式,策略模式,业务场景,过程)