策略模式

该文档是:设计模式学习...

博客连接:https://www.loveuluo.cn

日期:2020-01-30

1. 未使用策略模式时

image-20210130210544483

要实现的功能:

有一家游戏公司,制作一款鸭子游戏;在这个鸭子游戏中,角色都是鸭子,不同的鸭子之间,有共性,所以为了提高代码的重用性,开发人员就制作了一个鸭子的父类;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 duck=new MallardDuck();
        duck.quack();
        duck.swim();
        duck.display();
    }
}

2. 未使用策略模式时-优化版本①

image-20210130210526702

要实现的功能:

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

缺点:

1、程序员就会想,我只需要在父类Duck中,添加一个fly方法,那么所有的Duck的子类,也都具备了fly方法。此时问题看似解决了,但实际上出现了更麻烦的问题:所有Duck的子类通通都会飞了。要知道,父类中的方法。并不是所有的子类都通用的!!!例如橡皮鸭就不能飞,结果因为继承了Duck,搞得橡皮鸭也能飞了。

2、那么橡皮鸭去重写fly方法让它不能飞,此时问题看似又解决了,问题是变化不断地出现。一会加个木头鸭子,一会加个鸭子超人。程序员就要在每次添加新的鸭子角色时,都要判断新的鸭子角色会不会叫,会不会飞,针对于不同的鸭子,要有不同的处理方法,这样也很麻烦。

//==========================抽象鸭子父类==========================
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("吱吱叫....");
    }

    //橡皮鸭不会飞,重写fly方法
    @Override
    public void fly() {
        System.out.println("橡皮鸭不会飞...");
    }

    @Override
    public void display() {
        System.out.println("外观是橡皮鸭!");
    }
}
//==========================测试方法==========================
public class AppTest {
    public static void main(String[] args) {
        Duck duck=new RubberDuck();
        duck.quack();
        duck.swim();
        duck.fly();
        duck.display();
    }
}

3. 未使用策略模式时-优化版本②

image-20210130213643535

要实现的功能:

我们希望那些不会飞得鸭子,压根就没有fly方法,不会叫的鸭子,没有quack方法。

把这2个经常在子类中变化的方法,从父类中抽出来,分成两个接口:Quackable,Flyable。

缺点:

1、这样问题解决了吗?没有!以前是每加入一个新的鸭子角色,程序员就要判断,这个新鸭子是否会飞或者叫,不会的就重写对应的方法。现在是每加入一个新的鸭子角色,程序员就要判断,这个新鸭子是否会飞或者叫,会的就实现对应的接口。

2、另外一个缺点是,这个flyable或者quackble接口没有重用性可言。例如每一个实现flyable接口的都要重新写一遍这个接口的方法的具体实现。例如不同的鸭子有不同的飞法和叫法。

//==========================经常变化的方法抽出来的接口==========================
interface Flyable{
    void fly();
}
interface Quackble{
    void quack();
}
//==========================抽象鸭子父类==========================
abstract class Duck{
    public void swim(){
        System.out.println("游泳...");
    }
    //这是鸭子的外观,鸭子的外观不一样,所以抽象,让子类去实现。
    public abstract void display();
}
//==========================真实鸭子子类==========================
//野鸭子
class MallardDuck extends Duck implements Flyable,Quackble{

    @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,Quackble{

    @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 Quackble{

    @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("外观是雕塑鸭子!");
    }
}
//==========================测试方法==========================
public class AppTest {
    public static void main(String[] args) {
        //橡皮鸭子(此时不能用多态,因为父类中没有quack方法)
        RubberDuck rubberduck=new RubberDuck();
        rubberduck.swim();
        rubberduck.quack();
        rubberduck.display();
        //野鸭子
        MallardDuck mallardDuck=new MallardDuck();
        mallardDuck.swim();
        mallardDuck.fly();
        mallardDuck.quack();
        mallardDuck.display();
    }
}

4. 使用策略模式

image-20210130223655700

优点:

1、代码的重用性提高了!

2、可以在运行时替换行为!

3、有扩展性,想要新的方法,直接实现FlyBehavior接口即可。

策略模式具体实现:

不再用真实鸭子类直接实现Flyable或者Quackble的接口,用具体的方法实现去实现接口。然后把这个FlyBehavior接口组合到 抽象父类Duck中,这时候真实鸭子子类例如野鸭子只需要在无参构造器中直接把 具体的方法实现实现的类(FlyWithWings)直接赋值给从Duck父类继承来的FlyBehavior。这样就能调用对应的方法了。

interface FlyBehavior{
    void fly();
}
class FlyWithWings implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("插上翅膀");
        System.out.println("给翅膀染色");
        System.out.println("准备起飞~");
        System.out.println("用翅膀飞~~~~");
    }
}
class FlyWithRocket implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("背上帮个火箭飞~~~~");
    }
}
class FlyWithKick implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("人来了");
        System.out.println("准备抬腿");
        System.out.println("被人一脚踢飞了~~~~");
    }
}

interface QuickBehavior{
    void quack();
}
class Quack implements QuickBehavior{
    @Override
    public void quack() {
        System.out.println("嘎嘎叫");
    }
}
class Squeck implements QuickBehavior{
    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }
}
class MuteQuack implements QuickBehavior{
    @Override
    public void quack() {
        System.out.println("叫不出声了");
    }
}

抽象鸭子父类:

abstract class Duck{
    //弄成protected子类就能直接访问了
    protected FlyBehavior fb;
    protected QuickBehavior qb;

    //写上set方法 这样的话就可以在运行时替换行为
    public void setFb(FlyBehavior fb) {
        this.fb = fb;
    }
    public void setQb(QuickBehavior qb) {
        this.qb = qb;
    }
    
    //这里的飞或者叫没写死,得看他们指向谁
    public void performFly(){
        fb.fly();
    }
    public void performQuack(){
        qb.quack();
    }

    public void swim(){
        System.out.println("游泳...");
    }
    //这是鸭子的外观,鸭子的外观不一样,所以抽象,让子类去实现。
    public abstract void display();
}

真实鸭子子类:

//野鸭子
class MallardDuck extends Duck {

    public MallardDuck(){
        //野鸭子会用翅膀飞~~~和嘎嘎叫~~~
        /*这里这样看起来可能构造器重复了,每个构造器都要这样写,但其实FlyWithWings或者Quack这些飞或者叫的具体实现一般来说不只是一行两行代码
          例如用翅膀飞的代码可能是先 画上翅膀、插上羽毛、涂上颜色... 所以构造器的重复不算什么。
         */
        this.fb=new FlyWithWings();//这里的fb是从父类继承过来的,父类的修饰符是protected
        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 FlyWithRocket();
        this.qb=new Squeck();
    }

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

}
//不会飞也不会叫的雕刻鸭子,被人一脚踢飞
class DecoyDuck extends Duck{

    public DecoyDuck(){
        this.fb=new FlyWithKick();
        this.qb=new MuteQuack();
    }

    @Override
    public void display() {
        System.out.println("外观是雕塑鸭子!");
    }
}

测试方法:

public class AppTest {
    public static void main(String[] args) {
        Duck mallardDuck=new MallardDuck();
        mallardDuck.performFly();
        mallardDuck.performQuack();
        mallardDuck.swim();
        mallardDuck.display();
        System.out.println("==============");
        RubberDuck rubberDuck = new RubberDuck();
        rubberDuck.performFly();
        rubberDuck.performQuack();
        rubberDuck.swim();
        rubberDuck.display();
        System.out.println("==============");
        DecoyDuck decoyDuck = new DecoyDuck();
        decoyDuck.performFly();
        decoyDuck.performQuack();
        decoyDuck.swim();
        decoyDuck.display();
        System.out.println("==============");
        RedHeadDuck redHeadDuck = new RedHeadDuck();
        redHeadDuck.performFly();
        redHeadDuck.performQuack();
        redHeadDuck.swim();
        redHeadDuck.display();
    }
}

测试结果:

image-20210130223143181

演示能在运行时替换行为测试:

public class AppTest {
    public static void main(String[] args) {
        Duck mallardDuck=new MallardDuck();
        mallardDuck.performFly();
        System.out.println("===更改行为===");
        //传入FlyBehavior接口的具体实现就能运行时改变行为
        mallardDuck.setFb(new FlyWithKick());
        mallardDuck.performFly();
        System.out.println("===再次更改行为===");
        mallardDuck.setFb(new FlyWithRocket());
        mallardDuck.performFly();
    }
}

image-20210130225130449

最后修改:2021 年 01 月 31 日 10 : 39 AM
如果觉得我的文章对你有用,请随意赞赏