设计模式-模板方法模式
定义:一个抽象类公开定义了执行它的方法的方式/模板,而一些特殊方法的实现可以在子类中重写实现,但调用将以抽象类中定义的方式进行。
模板方法使得子类在不改变一个方法的结构前提下,重新定义该方法内部调用的一些方法,来得到一个新的执行结果。
如简历模板、论文模板,下载一个模板后不需要修改模块的位置,按顺序将自己的内容填写到相应的模块中,最终每个人都能制作出来一个属于自己的独一无二的简历、论文等。
依然以汽车为例,现在奔驰小轿车和宝马小轿车都生产好了,开始测试行驶
虽然轿车品牌不同,但是行驶测试的方式都是大同小异的,驾驶员测试步骤如下
(1) 补充燃料
(2) 检查车辆周围是否有障碍物
(3) 调整座椅
(4) 调整后视镜
(5) 调整方向盘
(6) 启动发动机
(7) 检查车灯
(8) 挂挡起步
由于车辆设计不同,奔驰和宝马车的一些细节操作可能有所不同,奔驰车的档位和座椅的使用比较特殊,宝马的方向盘调整比较特殊。
下面定义一个驾驶员模板基类,在模板基类的 DrivingTest 方法中固定了测试方法顺序,不能修改
一个奔驰驾驶员、一个宝马驾驶员 分别继承 驾驶员模板基类,根据自己的需求重写基类中可以重写的方法。
驾驶员模板基类
// 驾驶员基类
public abstract class DriverTemplate
{
// 补充燃料
protected virtual void Refuel()
{
Console.WriteLine("补充燃料");
}
// 车周围障碍物检查
protected virtual void Obstacle()
{
Console.WriteLine("车周围障碍物检查");
}
// 调整座椅
protected virtual void AdjustSeat()
{
Console.WriteLine("调整座椅");
}
// 调整后视镜
protected virtual void AdjustRearviewMirror()
{
Console.WriteLine("调整后视镜");
}
// 调整方向盘
protected virtual void AdjustSteeringWheel()
{
Console.WriteLine("调整方向盘");
}
// 启动发动机
protected virtual void StartTheEngine()
{
Console.WriteLine("启动发动机");
}
// 检查车灯
protected virtual void CheckLamp()
{
Console.WriteLine("检查车灯");
}
// 挂挡起步
protected virtual void StartsGear()
{
Console.WriteLine("挂挡起步");
}
// 行驶测试,固定逻辑,不允许子类重写
public void DrivingTest()
{
// 补充燃料
Refuel();
// 车周围障碍物检查
Obstacle();
// 调整座椅
AdjustSeat();
// 调整后视镜
AdjustRearviewMirror();
// 调整方向盘
AdjustSteeringWheel();
// 启动发动机
StartTheEngine();
// 检查车灯
CheckLamp();
// 挂挡起步
StartsGear();
}
}
奔驰驾驶员
// 奔驰驾驶员
public class BenzDriver : DriverTemplate
{
// 奔驰座椅调整比较特殊,子类中重写该方法
protected override void AdjustSeat()
{
Console.WriteLine("奔驰的座椅调整方式");
}
// 奔驰的档位比较特殊,子类中重写改方法
protected override void StartsGear()
{
Console.WriteLine("奔驰的档位操作方式 \n");
}
}
宝马驾驶员
// 宝马驾驶员
public class BMWDriver : DriverTemplate
{
// 宝马的方向盘调整比较特殊,子类中重写该方法
protected override void AdjustSteeringWheel()
{
Console.WriteLine("宝马的方向盘调整方式");
}
}
代码调用如下
class Customer
{
public Customer()
{
BenzDriver benzDriver = new BenzDriver();
benzDriver.DrivingTest();
BMWDriver bMWDriver = new BMWDriver();
bMWDriver.DrivingTest();
}
}
优点:
(1) 实现了代码复用,封装了不变部分,扩展可变部分。
(2) 灵活应对子类的变化,符合开闭原则。
(3) 提取公共部分代码,便于维护。
缺点
(1) 引入了抽象类,如果具体实现过多,需要开发人员花费更多的时间去理清类之间的关系。
(2) 抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,增加了阅读难度。