前言
对白很有趣,而且营造地很有氛围,如果还不会设计模式的话是值得一读的。
本笔记当然不会有那些有趣的图片和氛围,内容也会尽量浓缩。
策略模式(Strategy)
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
栗子
某公司做了一款鸭子游戏,游戏中的鸭子可以游泳,可以呱呱叫。系统设计了一个鸭子抽象类,让各种鸭子都继承它:
abstract class Duck {
void quack() {
System.out.println("呱呱叫");
}
void swim() {
System.out.println("游泳");
}
abstract void display();
}
其中一些鸭子:
class MallardDuck extends Duck {
@Override
void display() {
System.out.println("绿头鸭子");
}
}
class RedheadDuck extends Duck {
@Override
void display() {
System.out.println("红头鸭子");
}
}
提需求
现在公司要游戏中的鸭子能飞。
最简单的方案:给鸭子抽象类加上 fly 方法,这样鸭子们都能飞了。
void fly() {
System.out.println("飞");
}
对于真鸭子来说,该方法没有多大问题。但对于假鸭子来说,该方法就不对了,比如橡皮鸭,根本就不能飞,而且另一个 quack 方法也不适用于橡皮鸭,因为它是吱吱叫的。
当然,对于橡皮鸭,你可以覆盖 quack 方法和 fly 方法:
class RubberDuck extends Duck {
@Override
void quack() {
System.out.println("吱吱叫");
}
@Override
void display() {
System.out.println("橡皮鸭");
}
@Override
void fly() {
// 什么都不做
}
}
但如果现在又新添加了一只诱饵鸭呢?它是木头假鸭,既不会飞又不会叫,如果还是使用上面的方法的话就是:
class DecoyDuck extends Duck {
@Override
void quack() {
// 什么都不做
}
@Override
void display() {
System.out.println("诱饵鸭");
}
@Override
void fly() {
// 什么都不做
}
}
给程序员很明显的感觉就是,我都不需要这些什么都不做的行为,多么冗余的代码,但不覆盖又不行,不然这些鸭子的行为就不对了。所以利用继承来提供鸭子的行为导致了以下缺点:
- 代码在多个子类中重复。
- 运行时的行为不容易改变。
- 很难知道所有鸭子的全部行为。
- 改变会牵一发动全身,造成其他鸭子不想要的改变。
满足需求
设计原则一:找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起。
既然鸭子的行为是变化的,那么根据原则一,我们要做的就是把鸭子的行为从鸭子抽象类 Duck 中抽出来,那抽出来后怎么定义具体鸭子的行为呢?那当然是定义具体鸭子的同时,定义其行为。
设计原则二:针对接口编程,而不是针对实现编程。
设计原则三:多用组合,少用继承。
根据原则二,写出对应的飞和叫这两种行为的接口:
飞的行为:
interface FlayBehavior {
void fly();
}
具体飞的动作的两个实现:
class FlyWithWings implements FlayBehavior {
@Override
public void fly() {
System.out.println("飞");
}
}
class FlyNoWay implements FlayBehavior {
@Override
public void fly() {
// 不会飞,什么都不做
}
}
叫的行为:
interface QuackBehavior {
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() {
// 不会叫,什么都不做
}
}
给鸭子抽象类添加这些行为,并且加入 setter,让这些行为可以动态设置:
abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
// 如果想要改变鸭子的飞行
void setFlyBehavior(FlyBehavior behavior) {
this.flyBehavior = behavior;
}
void setQuackBehavior(QuackBehavior behavior) {
this.quackBehavior = behavior;
}
void quack() {
quackBehavior.quack();
}
void fly() {
flyBehavior.fly();
}
void swim() {
System.out.println("游泳");
}
abstract void display();
}
然后就可以在构造器里实现具体行为了:
class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
@Override
void display() {
System.out.println("绿头鸭子");
}
}
class RubberDuck extends Duck {
public RubberDuck() {
quackBehavior = new Squeak();
flyBehavior = new FlyNoWay();
}
@Override
void display() {
System.out.println("橡皮鸭子");
}
}
测试
public class Main {
public static void main(String[] args) throws IOException {
RubberDuck rubberDuck = new RubberDuck();
rubberDuck.display();
rubberDuck.fly();
rubberDuck.quack();
// 橡皮鸭也变成呱呱叫
QuackBehavior quack = new Quack();
rubberDuck.setQuackBehavior(quack);
rubberDuck.quack();
System.out.println("--------");
MallardDuck mallardDuck = new MallardDuck();
mallardDuck.display();
mallardDuck.fly();
mallardDuck.quack();
}
}
输出:
橡皮鸭子
橡皮鸭吱吱叫
呱呱叫
——–
绿头鸭子
飞
呱呱叫