《Head First 设计模式》笔记1

前言

对白很有趣,业务情景营造地很有氛围,如果还不会设计模式的话是值得一读的。

本笔记当然不会有那些有趣的图片和氛围,内容也会尽量浓缩。

策略模式(Strategy)

定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

栗子

某公司做了一款鸭子游戏,游戏中的鸭子可以游泳,可以呱呱叫。系统设计了一个鸭子抽象类,让各种鸭子都继承它:

1
2
3
4
5
6
7
8
9
10
11
abstract class Duck {
void quack() {
System.out.println("呱呱叫");
}

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

abstract void display();
}

其中一些鸭子:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MallardDuck extends Duck {
@Override
void display() {
System.out.println("绿头鸭子");
}
}

class RedheadDuck extends Duck {
@Override
void display() {
System.out.println("红头鸭子");
}
}

提需求

现在公司要游戏中的鸭子能飞。

最简单的方案:给鸭子抽象类加上 fly 方法,这样鸭子们都能飞了。

1
2
3
void fly() {
System.out.println("飞");
}

对于真鸭子来说,没多大问题。但对于假鸭子来说,就不对了,比如橡皮鸭,根本就不能飞,而且另一个 quack 方法也不适用于橡皮鸭,因为它是吱吱叫的。

当然,对于橡皮鸭,你可以覆盖 quack 方法和 fly 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class RubberDuck extends Duck {
@Override
void quack() {
System.out.println("吱吱叫");
}

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

@Override
void fly() {
// 什么都不做
}
}

但如果现在又新添加了一只诱饵鸭呢?它是木头假鸭,既不会飞又不会叫,如果还是使用上面的方法的话就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DecoyDuck extends Duck {
@Override
void quack() {
// 什么都不做
}

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

@Override
void fly() {
// 什么都不做
}
}

给程序员很明显的感觉就是,并不需要这些什么都不做的行为,但不覆盖又不行,不然这些鸭子的行为就不对了。所以利用继承来提供鸭子的行为有以下缺点:

  • 代码在多个子类中重复。
  • 运行时的行为不容易改变。
  • 很难知道所有鸭子的全部行为。
  • 改变会牵一发动全身,造成其他鸭子不想要的改变。

满足需求

设计原则一:找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起。

既然鸭子的行为是变化的,那么根据原则一,我们要做的就是把鸭子的行为从鸭子抽象类 Duck 中抽出来,那抽出来后怎么定义具体鸭子的行为呢?那当然是定义具体鸭子的同时,定义其行为。

设计原则二:针对接口编程,而不是针对实现编程。
设计原则三:多用组合,少用继承。

根据原则二,写出对应的飞和叫这两种行为的接口:

飞的行为:

1
2
3
interface FlayBehavior {
void fly();
}

具体飞的动作的两个实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
class FlyWithWings implements FlayBehavior {
@Override
public void fly() {
System.out.println("飞");
}
}

class FlyNoWay implements FlayBehavior {
@Override
public void fly() {
// 不会飞,什么都不做
}
}

叫的行为:

1
2
3
interface QuackBehavior {
void quack();
}

具体叫的动作的三个实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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,让这些行为可以动态设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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();
}

然后就可以在构造器里实现具体行为了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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("橡皮鸭子");
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}
}

输出:
橡皮鸭子
橡皮鸭吱吱叫
呱呱叫
——–
绿头鸭子

呱呱叫

Author

Zoctan

Posted on

2018-03-28

Updated on

2023-03-14

Licensed under