大话设计模式
1 模板方法模式(TemplateMethod)结构图
2 对该模式的一些解释
2.1 概念:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
2.2 实质就是利用多态性实现代码的复用!
- 模板方法模式是通过把不变形为搬到超类,去除子类中的重复代码来体现它的优势。
- 当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬到单一的地方,这样就帮助子类拜托重复的不变行为的纠缠。
2.3 使用场景:当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同的时候,我们通常考虑用模板方法模式来处理。
2.4 原型模式代码
AbstractClass是一个抽象类,其实也就是一抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体方法,他给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类中来实现。顶级逻辑也可能调用一些具体方法。
class AbstractClass
{
public:
//一些抽象行为,方法子类中去实现
virtual void Operation1();
virtual void Operatiron2();
//模板方法,给出了逻辑的框架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类中去实现
void TemplateMethod()
{
Operation1();
Operatiron2();
}
};
ConcreteClass,实现父类所定义的一个或者多个抽象方法。
class ConcreteClassA :public AbstractClass
{
public:
void Operation1() override
{
cout << "类A具体实现方法一" << endl;
}
void Operation2() override
{
cout << "类A具体实现方法二" << endl;
}
};
class ConcreteClassB :public AbstractClass
{
public:
void Operation1() override
{
cout << "类B具体实现方法一" << endl;
}
void Operation2() override
{
cout << "类B具体实现方法二" << endl;
}
};
3 C++代码实现
题目:老师出一份考试试卷,让不同的学生来作答;
#include<iostream>
#include<string>
using namespace std;
class TestPaper
{
protected:
string name;
string NO;
public:
TestPaper(const string na, const string no) :name(na), NO(no){}
void setName(string name)
{
this->name = name;
}
string getName()
{
return this->name;
}
void setNO(string NO)
{
this->NO = NO;
}
string getNO()
{
return this->NO;
}
void Question1()
{
cout << "以下四个选项哪个是不正确的【】" << endl;
cout << "A:派生类一般都用共有派生" << endl;
cout << "B:对基类成员的访问必须是无二义性的" << endl;
cout << "C:赋值兼容规则则适用于多重继承的组合" << endl;
cout << "D:基类的公有成员在派生类中仍然是共有的" << endl;
cout << "\n答案:" << Answer1() << endl;
}
void Question2()
{
cout << "\n所谓数据封装就是将一组数据和与这组数据相关的操作组装在一起,形成一个实体,这个实体也就是【】" << endl;
cout << "A:类" << endl;
cout << "B:对象" << endl;
cout << "C:函数体" << endl;
cout << "D:数据块" << endl;
cout << "\n答案:" << Answer2() << endl;
}
virtual char Answer1()
{
return ' ';
}
virtual char Answer2()
{
return ' ';
}
void Show()
{
cout << "\t\t姓名:" << name << "\t学号:" << NO << endl << endl;
cout << "答案:";
Question1();
Question2();
cout << endl;
}
};
class Student1 :public TestPaper
{
public:
Student1(const string name, const string NO) :TestPaper(name, NO){}
char Answer1() override
{
return 'D';
}
char Answer2() override
{
return 'A';
}
};
class Student2 :public TestPaper
{
public:
Student2(const string name, const string NO) :TestPaper(name, NO){}
char Answer1() override
{
return 'C';
}
char Answer2() override
{
return 'B';
}
};
int main()
{
TestPaper *student1 = new Student1("小明", "16011");
TestPaper *student2 = new Student2("小王", "16012");
student1->Show();
student2->Show();
if (student1)
{
delete student1;
student1 = nullptr;
}
if (student2)
{
delete student2;
student2 = nullptr;
}
system("pause");
return 0;
}
运行结果:
姓名:小明 学号:16011
答案:以下四个选项哪个是不正确的【】
A:派生类一般都用共有派生
B:对基类成员的访问必须是无二义性的
C:赋值兼容规则则适用于多重继承的组合
D:基类的公有成员在派生类中仍然是共有的
答案:D
所谓数据封装就是将一组数据和与这组数据相关的操作组装在一起,形成一个实体,这个实体也就是【】
A:类
B:对象
C:函数体
D:数据块
答案:A
姓名:小王 学号:16012
答案:以下四个选项哪个是不正确的【】
A:派生类一般都用共有派生
B:对基类成员的访问必须是无二义性的
C:赋值兼容规则则适用于多重继承的组合
D:基类的公有成员在派生类中仍然是共有的
答案:C
所谓数据封装就是将一组数据和与这组数据相关的操作组装在一起,形成一个实体,这个实体也就是【】
A:类
B:对象
C:函数体
D:数据块
答案:B
请按任意键继续. . .
以下内容引自<http://blog.youkuaiyun.com/zhengzhb/article/details/7405608>,转载请注明出处!!!
卡奴达摩----模板方法模式
定义:定义一个操作中算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤。
类型:行为类模式
类图:
abstract class AbstractSort {
/**
* 将数组array由小到大排序
* @param array
*/
protected abstract void sort(int[] array);
public void showSortResult(int[] array){
this.sort(array);
System.out.print("排序结果:");
for (int i = 0; i < array.length; i++){
System.out.printf("%3s", array[i]);
}
}
}
class ConcreteSort extends AbstractSort {
@Override
protected void sort(int[] array){
for(int i=0; i<array.length-1; i++){
selectSort(array, i);
}
}
private void selectSort(int[] array, int index) {
int MinValue = 32767; // 最小值变量
int indexMin = 0; // 最小值索引变量
int Temp; // 暂存变量
for (int i = index; i < array.length; i++) {
if (array[i] < MinValue){ // 找到最小值
MinValue = array[i]; // 储存最小值
indexMin = i;
}
}
Temp = array[index]; // 交换两数值
array[index] = array[indexMin];
array[indexMin] = Temp;
}
}
写好后交给A,A拿来一运行:
public class Client {
public static int[] a = { 10, 32, 1, 9, 5, 7, 12, 0, 4, 3 }; // 预设数据数组
public static void main(String[] args){
AbstractSort s = new ConcreteSort();
s.showSortResult(a);
}
}
运行结果:
排序结果: 0 1 3 4 5 7 9 10 12 32
运行正常。行了,任务完成。没错,这就是模版方法模式。大部分刚步入职场的毕业生应该都有类似B的经历。一个复杂的任务,由公司中的牛人们将主要的逻辑写好,然后把那些看上去比较简单的方法写成抽象的,交给其他的同事去开发。这种分工方式在编程人员水平层次比较明显的公司中经常用到。比如一个项目组,有架构师,高级工程师,初级工程师,则一般由架构师使用大量的接口、抽象类将整个系统的逻辑串起来,实现的编码则根据难度的不同分别交给高级工程师和初级工程师来完成。怎么样,是不是用到过模版方法模式?
模版方法模式的结构,模版方法模式由一个抽象类和一个(或一组)实现类通过继承结构组成,抽象类中的方法分为三种:
- 抽象方法:父类中只声明但不加以实现,而是定义好规范,然后由它的子类去实现。
- 模版方法:由抽象类声明并加以实现。一般来说,模版方法调用抽象方法来完成主要的逻辑功能,并且,模版方法大多会定义为final类型,指明主要的逻辑功能在子类中不能被重写。
- 钩子方法:由抽象类声明并加以实现。但是子类可以去扩展,子类可以通过扩展钩子方法来影响模版方法的逻辑。
- 抽象类的任务是搭建逻辑的框架,通常由经验丰富的人员编写,因为抽象类的好坏直接决定了程序是否稳定性。
实现类用来实现细节。抽象类中的模版方法正是通过实现类扩展的方法来完成业务逻辑。只要实现类中的扩展方法通过了单元测试,在模版方法正确的前提下,整体功能一般不会出现大的错误。
模版方法的优点及适用场景
- 容易扩展。一般来说,抽象类中的模版方法是不易反生改变的部分,而抽象方法是容易反生变化的部分,因此通过增加实现类一般可以很容易实现功能的扩展,符合开闭原则。
- 便于维护。对于模版方法模式来说,正是由于他们的主要逻辑相同,才使用了模版方法,假如不使用模版方法,任由这些相同的代码散乱的分布在不同的类中,维护起来是非常不方便的。
- 比较灵活。因为有钩子方法,因此,子类的实现也可以影响父类中主逻辑的运行。但是,在灵活的同时,由于子类影响到了父类,违反了里氏替换原则,也会给程序带来风险。这就对抽象类的设计有了更高的要求。