引言
模板方法模式和策略模式都是行为设计模式,它们的目标都是通过封装算法来提高代码的灵活性和可维护性。然而,它们的实现方式和适用场景有所不同。下面就会介绍这两种设计模式
模板方法模式
模板方法的核心思想就是,将操作步骤固定(相当于给你了一个模板),而实现这些步骤的方法是可以替换的。
- 举个例子,班主任想要对班级同学的成绩进行统计并进行排序,学校规定使用了一种软件,这种软件需要人为完成一些步骤才能实现统计。
- 将成绩登记到电脑上
- 对数据进行排序
- 公布成绩(不要这样做!!!不要打击学生的自尊心)
- 我们必须严格按照学校的要求来做,但是我们可以通过不同的方法来完成学校的要求
- 对于登记到电脑上
- 这好像只能将成绩一个一个登记到电脑上,没有其他办法了。
- 对数据进行排序
- 最后公布成绩
- 我们可以只公布分数而不公布姓名
- 我们也可以将分数和姓名一起公布(要被学生骂死了)
言归正传,这种情况下学校使用的软件相当于一个模板方法,但是有些步骤我们可以选择不同的操作方式。这就是模板方法的思想。
代码展现
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
|
class Base {
public:
Base() = default;
virtual ~Base() = default;
void DoWork() {
Step1();Step2();Step3();
Step4(); //钩子操作,空操作,由子类选择是否可以扩展这个操作
}
private:
static void Step1() {std::cout << "step1" << std::endl;}
static void Step3() {std::cout << "step3" << std::endl;}
protected:
virtual void Step2() = 0;
virtual void Step4() {}
};
//用户来决定使用哪些方法。
class Derived1 : public Base {
~Derived1() override = default;
void Step2() override {std::cout << "derived1's step2" << std::endl;}
};
class Derived2 : public Base {
~Derived2() override = default;
void Step2() override {std::cout << "derived2's step2" << std::endl;}
void Step4() override {std::cout << "extend the hook operator" << std::endl;}
};
int main() {
Base* p1 = new Derived1();
Base* p2 = new Derived2();
p1->DoWork();
std::cout << "------------------" << std::endl;
p2->DoWork();
delete p1;
delete p2;
return 0;
}
|
- 上面的模板函数给出了四个step,其中step2已经被模板方法写死了,你只能这么做(非虚函数),但是有些step方法没有实现(纯虚函数),给了你自由度选择自己的操作方式。
- 我们可以看到有一个step4钩子操作,这个构造操作在基类里面通常是个空操作,用来说明在这个地方你可以选择进行一些操作来进行优化,但是如果你优化也ok,这取决与你自己的意愿。
- 这样用户就可以定义派生类来继承基类,重写方法来决定一要用什么具体的步骤,你也可以重写上面step4操作进行操作上的一些扩展。
策略模式
策略模式提供了一种切换算法的思想,对于一个功能,我们可以切换里面的算法来实现不同的功能。对于策略模式,程序员可以先实现不同的策略,等到需要切换策略的时候 ,换一种策略即可。
代码展现
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
// strategy
class CalcStrategy {
public:
virtual ~CalcStrategy() = default;
virtual int calc(int a, int b) = 0;
};
//用户拓展
class AddStrategy final : public CalcStrategy {
int calc(const int a, const int b) override { return a + b;}
};
class SubStrategy final : public CalcStrategy {
int calc(const int a, const int b) override { return a - b;}
};
class MulStrategy final : public CalcStrategy {
int calc(const int a, const int b) override { return a * b;}
};
class DivStrategy final : public CalcStrategy {
int calc(const int a, const int b) override { return a / b;}
};
//factory
class Factory {
public:
virtual ~Factory() = default;
virtual CalcStrategy* NewStrategy() =0;
};
//用户拓展工厂方法
class AddFactory final : public Factory {
CalcStrategy* NewStrategy() override {return new AddStrategy;}
};
class SubFactory final : public Factory {
CalcStrategy* NewStrategy() override {return new SubStrategy;}
};
class MulFactory final : public Factory {
CalcStrategy* NewStrategy() override {return new MulStrategy;}
};
class DivFactory final : public Factory {
CalcStrategy* NewStrategy() override {return new DivStrategy;}
}; //end of factory
class Calculator {
public:
explicit Calculator(Factory* strategy_factory) {strategy_ = strategy_factory->NewStrategy();}
int doCalc(const int a, const int b) const { return strategy_->calc(a, b);}
~Calculator() { delete strategy_;}
private:
//对于策略,采用的是组合的方式来进行,可以切换不通的策略
CalcStrategy* strategy_;
}; //end of strategy
int main() {
Factory* add = new AddFactory();
Factory* sub = new SubFactory();
Factory* mul = new MulFactory();
Factory* div = new DivFactory();
const Calculator calculator1(add);
std::cout << calculator1.doCalc(1,1) << std::endl;;
const Calculator calculator2(sub);
std::cout << calculator2.doCalc(1,1) << std::endl;;
const Calculator calculator3(mul);
std::cout << calculator3.doCalc(1,1) << std::endl;;
const Calculator calculator4(div);
std::cout << calculator4.doCalc(1,1) << std::endl;;
delete factory1;
delete factory2;
delete factory3;
delete factory4;
}
|
- 可以看出,我们有一个Calculator类用来实现一个计算的功能。
- Calculater 里面组合了一个Strategy类(可以认为功能和实现功能的策略是紧密联系的),我们可以传入不同的策略类来切换策略实现不同的功能
- 在本例中,我们可以通过工厂方法创建不同的策略,在创建Strategy对象的时候,传入不同的策略从而达到实现不同的功能的目的。
两种模式的对比
最后,我们再来看一下这两种模式的对比
| 对比维度 |
模板方法模式 |
策略模式 |
| 定义 |
定义算法的框架,子类可以重写部分步骤,但不改变算法结构。 |
定义一系列算法,封装每个算法,使它们可以互换。 |
| 核心思想 |
将算法的通用部分放在父类中,具体实现延迟到子类。 |
将算法的选择与使用分离,客户端可以动态选择不同的策略。 |
| 实现方式 |
基于继承,父类定义算法框架,子类实现具体步骤。 |
基于组合,通过接口或抽象类定义策略,具体策略由不同类实现。 |
| 灵活性 |
较低,算法的结构在父类中固定,子类只能改变部分步骤。 |
较高,客户端可以动态切换策略,算法可以完全替换。 |