设计模式——策略模式

157 阅读3分钟

策略模式属于行为型模式

这里以一个例子来进行引入并讲解。该例子是一个鸭子游戏,游戏中会出现各种各样的鸭子,它们会游泳和呱呱叫,这样我们会发现,它们的共同特征是游泳和呱呱叫,所以我们可以设计一个超类,然后其它子类继承该超类即可。

在这里我们对超类进行设计时,需要有三个方法,其一是swim(),其二是quack(),其三就是外观display()了,这里的外观是各种各样的,所以外观需要是抽象的,这里给出超类的源码

public abstract class Duck {
    public void quack() {
        System.out.println("呱呱叫");
    }

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

    public abstract void display();
}

有了超类,我们就可以设计子类了,如果我们想要设计一只绿头鸭和一只红头鸭,那么只需继承超类即可

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

这样看似没什么问题,但是,如果有的鸭子不会叫怎么办呢,你可能会想,让不会叫的鸭子重写quack()方法,如果将来又有鸭子会飞呢,你可能会在超类中加上fly()方法,但是有的鸭子又不会飞,你又要重写该fly()方法,这里给出超类和一个既不会叫也不会飞的子类鸭子


public abstract class Duck {
    public void quack() {
        System.out.println("呱呱叫");
    }
    
    public void fly() {
        System.out.println("飞");
    }

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

    public abstract void display();
}
public class NoQuackNoFly extends Duck {
    @Override
    public void display() {
        System.out.println("我不会叫也不会飞");
    }
    
    //因为既不会叫也不会飞,所以只能空实现
    @Override
    public void quack() {
    }
    @Override
    public void fly() {
    }
}

如果后面还有更多的方法是不是都需要重写一遍,这看似有点麻烦了,所以我们可以将这些非共有的方法进行分离开来,然接口来处理这些方法,有该方法的我们就去实现,没有的就不用实现了,改完之后的Duck(),以及两个接口如下


public abstract class Duck {

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

    public abstract void display();
}
public interface Quack {
    public void quack();
}
public interface Fly {
    public void fly();
}

这样一来,我们再去实现既不会飞也不会叫的鸭子就相对简单了,如下

public class NoQuackNoFly extends Duck {
    @Override
    public void display() {
        System.out.println("我不会叫也不会飞");
    }
}

这看似很棒,但是,如果有1024只鸭子,它们都重写了quack方法,而且都是呱呱叫,如果我们想让呱呱叫改为吱吱叫,那你是不是要改1024个类,这貌似有点笨拙,所以我们引入了我们的策略模式进行解决

首先我们有一个超类,也就是Duck,里面有两个 固定的方法,一个是游泳,一个是外观


public abstract class Duck {

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

    public abstract void display();
}

然后是叫和飞的接口

public interface Quack {
    public void quack();
}
public interface Fly {
    public void fly();
}

这里我们用不同方法的类去实现这两个接口,你会看到神奇的效果的

public class QuackImpl implements Quack {
    @Override
    public void quack() {
        System.out.println("我会呱呱叫");
    }
}
public class SquackImpl implements Quack {
    @Override
    public void quack() {
        System.out.println("我会吱吱叫");
    }
}

 

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

接口的实现类写好之后,我们以叫声来进行测试,我们以绿头鸭是呱呱叫红头鸭是吱吱叫为例,此刻我们修改Duck类,添加对叫声的引用


public abstract class Duck {

    Quack quack;

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

    public abstract void display();
}

然后我们去修改绿头鸭和红头鸭的构造方法,在构造方法中new出叫声的实例对象,也就构成了,当你调用quack()方法时,我们好知道是调用的呱呱叫还是吱吱叫

public class MallardDuck extends Duck {

    public MallardDuck() {
        quack = new QuackImpl();
    }

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

    public RedheadDuck() {
        quack = new SquackImpl();
    }

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

好了,这里给出测试类Main


public class Main {
    public static void main(String[] args) {
        Duck duck = new MallardDuck();
        duck.performQuack();

        Duck duck1 = new RedheadDuck();
        duck1.performQuack();
    }
}

运行结果如下