设计模式之策略模式和模版方法模式

今天学两个简单,容易理解又实用的设计模式

策略模式

描述:定义一个算法的系列,将其各个分装,并且使他们有交互性。策略模式使得算法在用户使用的时候能独立的改变。

策略模式

什么时候用策略模式?
策略模式是用来封装算法的,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了算法类和使用算法类之间的耦合。在实践中,我们发现可以用它封装几乎任何类型的规则,只要在分析过程中听到需要在不同时机应用不同的业务规则,就可以用策略模式处理这种变化的可能性。

策略模式的3个角色

  • Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。

  • Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。

  • ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

策略模式实现起来也十分简单,随便写个代码示范就好

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
//Strategy
abstract class FastFood {
abstract void getFood();
}

//ConcreteStrategy
class KFC extends FastFood {
@Override
void getFood() {
System.out.println("点开封菜!");
}
}

class McDonalds extends FastFood {
@Override
void getFood() {
System.out.println("点金拱门!");
}
}

//Context
class TakeawayPlatform {
FastFood fastFood;

public TakeawayPlatform(FastFood fastFood) {
this.fastFood = fastFood;
}

public void getFood() {
fastFood.getFood();
}
}

//策略模式结合简单工厂
class TakeawayFactory {
FastFood fastFood = null;

private final String KFC = "KFC";
private final String McDonalds = "McDonalds";

public TakeawayFactory(String type) {
switch (type) {
case KFC:
fastFood = new KFC();
break;
case McDonalds:
fastFood = new McDonalds();
break;
}
}

public void getFood() {
fastFood.getFood();
}
}

主程序

1
2
3
4
5
6
7
8
9
10
11
12
public class Strategy {
public static void main(String[] args) {
TakeawayPlatform takeawayKFC = new TakeawayPlatform(new KFC());
takeawayKFC.getFood();

TakeawayPlatform takeawayMCD = new TakeawayPlatform(new McDonalds());
takeawayMCD.getFood();

TakeawayFactory takeawayFactory = new TakeawayFactory("KFC");
takeawayFactory.getFood();
}
}

在基本策略模式中,选择ConcreteStrategy的职责由客户端承担,并转给Context对象。这并没有解除客户端需要选择判断的压力,而策略模式和简单工厂模式结合后,选择的职责也可以由Context承担,这最大化减轻了客户端的职责。

模版方法模式

描述:模板方法模式准备一个抽象类,将部分逻辑以具体方法及具体构造子类的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先构建一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

模版方法模式

什么时候用模版方法模式?
模版方法模式提供了很好的代码复用平台,当遇到一些列步骤构成的过程需要执行,这些过程从高层次上看是相似的,只是有些具体步骤实现可能不同,这时可以考虑使用模版方法模式。

模版方法模式的2个角色

  • AbstractClass(抽象类):在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。

  • ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。

一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地完全继承下来。模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中可以是具体方法,也可以是抽象方法。

基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。

  • 抽象方法:一个抽象方法由抽象类声明、由其具体子类实现。
  • 具体方法:一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
  • 钩子方法:可以与一些具体步骤 “挂钩” ,以实现在不同条件下执行模板方法中的不同步骤

做奶茶的基本工序大致是相同的,加奶加茶,顾客选择加料加冰甜度等等,最后塑封。用代码演示如下

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
//AbstractClass
abstract class MilkTea {
//模版方法,注意要加final让子类不能改写
protected final void makeMilkTea() {
addMilk();
addTea();
if (needIce()) {
addIce();
}
addStuff();
packUp();
}

final void addMilk() {
System.out.println("添加牛奶");
}

final void addTea() {
System.out.println("添加茶汁");
}

final void addIce() {
System.out.println("添加冰块");
}

final void packUp() {
System.out.println("塑封,完成!");
}

protected boolean needIce() {
return false;
}

abstract void addStuff();
}

//ConcreteClass
class PearlMilkTea extends MilkTea {
@Override
void addStuff() {
System.out.println("加珍珠");
}
}

class RedBeanMilkTeaWithIce extends MilkTea {
@Override
protected boolean needIce() {
return true;
}

@Override
void addStuff() {
System.out.println("加红豆");
}
}

主程序

1
2
3
4
5
6
7
8
9
10
11
public class TemplateMethod {
public static void main(String[] args) {
System.out.println("-----开始制作珍珠奶茶-----");
MilkTea pearl = new PearlMilkTea();
pearl.makeMilkTea();

System.out.println("-----开始制作红豆奶茶加冰-----");
MilkTea redBean = new RedBeanMilkTeaWithIce();
redBean.makeMilkTea();
}
}

当不变的和可变的行为在方法的子类实现混合在一起时,不变的行为就会在子类中重复出现。通过模版方法模式把不变的行为搬到单一的地方,这样帮助子类摆脱重复不变行为的纠缠

参考资料:

0%