0、行为型模式

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。

行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足”合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

行为型模式分为:

  • 模板方法模式
  • 策略模式
  • 命令模式
  • 职责链模式
  • 状态模式
  • 观察者模式
  • 中介者模式
  • 迭代器模式
  • 访问者模式
  • 备忘录模式
  • 解释器模式

以上11种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。


模板模式

1、豆浆制作问题

1)制作豆浆的流程:选材 => 添加配料 => 浸泡 => 放到豆浆机打碎

2)通过添加不同的配料,可以制作出不同口味的豆浆

3)选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的

4)请使用模板方法模式完成(因为模板方法模式比较简单,很容易就想到这个方案,因此就直接使用,不再使用传统的方案来引出模板方法模式)


2、模板模式的基本介绍

1)模板方法模式(Template Method Pattern),又叫模板模式(Template Pattren),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。

2)简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。

3)这种类型的设计模式属于行为型模式。


3、结构

模板方法(Template Method)模式包含以下主要角色:

1)抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

  • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

  • 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:

    — — 抽象方法(Abstract Method):一个抽象方法由抽象类声明、由其具体子类实现。

    — — 具体方法(Concrete Method):一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

    — — 钩子方法(Hook Method):在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。

2)具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。


4、应用实例代码实现

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 抽象类,表示豆浆
public abstract class SoyaMilk {
// 模板方法,make,模板方法可以做成final,不让子类去覆盖。
final void make() {
select();
addCondiments();
soak();
beat();
}

// 选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆");
}

// 添加不同的配料,抽象方法,子类具体实现
abstract void addCondiments();

// 浸泡
void soak() {
System.out.println()"第三步:黄豆和配料开始浸泡,需要3小时";
}

// 打碎
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎");
}
}

public class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println("加入上好的红豆");
}
}

public class PeanutSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println("加入上好的花生");
}
}

public class Client {
public static void main(String[] args) {
// 制作红豆豆浆
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
redBeanSoyaMilk.make();

// 制作花生豆浆
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();

}
}

5、模板方法模式的钩子方法

1)在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。

2)还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,请使用钩子方法对前面的模板方法进行改造。

3)代码演示

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 抽象类,表示豆浆
public abstract class SoyaMilk {
// 模板方法,make,模板方法可以做成final,不让子类去覆盖。
final void make() {
select();
if (customerWantCondiments()) {
addCondiments();
}

soak();
beat();
}

// 选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆");
}

// 添加不同的配料,抽象方法,子类具体实现
abstract void addCondiments();

// 浸泡
void soak() {
System.out.println()"第三步:黄豆和配料开始浸泡,需要3小时";
}

// 打碎
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎");
}

// 钩子方法,决定是否需要添加配料
boolean customerWantCondiments() {
return true;
}
}

public class PureSoyaMilk extends SoyaMilk {
@Override
boolean customerWantCondiments() {
return false;
}

@Override
void addCondiments() {

}
}

public class Client {
public static void main(String[] args) {
// 制作纯豆浆
SoyaMilk pure = new PureSoyaMilk();
pure.make();
}
}

6、模板模式在Spring框架中的应用

1)Spring IOC容器初始化时运用到的模板方法模式

ConfigurableApplicationContext类中的refresh()方法

2)代码分析


7、模板方法模式的注意事项

1)基本思想是:算法只存在于一个地方,也就是父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。

2)实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。

3)既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时类提供部分步骤的实现。

4)实现了反向控制,通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制,并符合”开闭原则”。

5)该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。

6)一般模板方法都加上final关键字,防止子类重写模板方法。

7)模板方法模式使用场景,当要完成在某个过程,该过程要执行一些列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。或者需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。