策略模式

157 阅读3分钟

引入

我们想设计一个小鸭子的类型,但是有些鸭子比如浴池里的小黄鸭它不会飞,纪念品店的小木鸭不会叫。各种各样的小鸭子们也有相似之处,如外形。

如果我们把这些行为都放在父类或都声明接口,很明显会增加代码量,很多代码无法复用,鸭子们虽然可能会叫得千姿百态,但明显不会飞得千姿百态。

解决办法就是使用良好的OO软件设计原则,针对有些行为会变有些行为固定的需求就采用针对接口编程

设计原则

  • 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  • 针对接口编程,而不是针对实现编程。
  • 多用组合,少用继承。

我们可以设计FlyBehaviorQuackBehavior接口表示行为,再用类去具体实现。这样可以复用,甚至鸟类的飞行行为我们也可以实现了,这些行为与鸭子类无关。

针对接口编程

其实其真正意义是针对超类型编程,其关键在于多态

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

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

具体实现

  • 如下要求我们必须实现具体编码
Dog d = new Dog();
d.bark();
  • 使用针对接口/超类型编程
Animal animal = new Dog();
animal.makeSound();

子类实例化的动作不需要在代码中硬编码。而是在运行时才指定具体实现的对象

a = getAnimal();
a.makeSound();
  • 整合 鸭子父类
public class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public void performQuack() {
        quackBehavior.quack();
    }
}

具体的Mallard鸭子

public class MallardDuck extends Duck {
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehaivor = new FlyWithWings();
    }
}

虽然这里创建了QuackFlyBehavior的实例,想要不具体实现就需要后续学习更多设计模式原理。

完整实例

  • 抽象父类——Duck
public abstract class Duck {

    FlyBehavior flyBehavior;
    QuackBehavior quackbehavior;
    public Duck() {
    
    }

    public abstract void display();

    public void performFly() {
        flyBehavior.fly();
    }
    
    public void performQuack() {
        quackbehavior.quack();
    }

    public void swim() {
        System.out.println("All duck float, even decoys");
    }
}
  • FlyBehavior接口及其两个实现类
public interface FlyBehavior {
    public void fly;
}
public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("I'm flying");
    }
}
public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("I can't fly");
    }
}
  • QuackBehavior接口及其三个实现类
public interface QuackBehavior {
    public void quack();
}
public class Quack implements QuackBehavior {
    public void quack() {
        System.out.println("Quack");
    }
}
public class MuteQuack implements QuackBehavior {
    public void quack() {
        System.out.println("<< Silence >>");
    }
}
public class Squeak implements QuackBehavior {
    public void quack() {
        System.out.println("Squeak");
    }
}
  • 测试
public class MiniDuckSimulator {
    public static void main (String[] args) {
        Duck mallard = new MallardDuck();
        mallard.performFly();
        mallard.performQuack();
    }
}

动态设定行为

(我想到了setter注入)\

  • Duck类里引入两个新方法
public void setFlyBehavior(FlyBehavior fb) {
    flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
    quackBehavior = qb;
}
  • 构造一个模型鸭类
public class ModelDuck extends Duck {
    public ModelDuck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Quack();
    }

    public void display() {
        System.out.println("I'm a model duck");
    }
}
  • 创建新的FlyBehavior
public class FlyRocketPowered implements FlyBehavior {
    public void fly() {
        System.out.println("I'm flying with a rock!");
    }
}
  • 测试
Duck model = new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();

利用setter方法改造鸭子

小结

根据这个实例可以再次理解一下IS-AHAS-AIMPLEMENTS这三个关系

组合建立系统具有很多的弹性,不仅可以将算法封装成类,更可以动态地改变行为,只要组合行为对象符合正确的接口标准即可。

致力于系统维护和变化,提高可维护性和可扩展性。