文章目录
1、模版方法介绍
2、模版方法类图
3、模版方法例子的实现以及“钩子”方法
4、模版方法模式总结
模板方法介绍:定义了一个算法的步骤,允许子类为一个或者多个步骤提供实现。
类图:
接下来让我们看下它的类图、~~~~
一个简单的Demo:
故事从三只企鹅开始的:
企鹅大胖,二虎,豆豆生活在遥远的南极,无忧无虑的生活着。而它们每天做的事就是 ->
/// <summary> /// 企鹅大胖 /// </summary> public class PenguinDaPang { public void SayHi(string Name, int Age) { Console.WriteLine($"我叫:{Name},我{Age}岁了,我每天要做的事是:"); } public void DoSomethingOnDay() {
SayHi("大胖",4); Eat(); Sleep(); BeatDoudou(); } public void Eat() { Console.WriteLine("我在吃金枪鱼"); } public void Sleep() { Console.WriteLine("我在睡觉"); } public void BeatDoudou() { Console.WriteLine("我在打豆豆"); } }
/// <summary> /// 企鹅二虎 /// </summary> public class PenguinErHu { public void SayHi(string Name, int Age) { Console.WriteLine($"我叫:{Name},我{Age}岁了,我每天要做的事是:"); } public void DoSomethingOnDay() {
SayHi("二虎",4); Eat(); Sleep(); BeatDoudou(); } public void Eat() { Console.WriteLine("我在吃三文鱼"); } public void Sleep() { Console.WriteLine("我在睡觉"); } public void BeatDoudou() { Console.WriteLine("我在打豆豆"); } }
/// <summary> /// 企鹅豆豆 /// </summary> public class PenguinDouDou { public void SayHi(string Name, int Age) { Console.WriteLine($"我叫:{Name},我{Age}岁了,我每天要做的事是:"); } public void DoSomethingOnDay() {
SayHi("豆豆",3); Eat(); Sleep(); //BeatDoudou(); } public void Eat() { Console.WriteLine("我在吃比目鱼"); } public void Sleep() { Console.WriteLine("我在睡觉"); } //我并不打自己。。 //public void BeatDoudou() //{ // Console.WriteLine("我在打豆豆"); //} }
没错,就是吃饭、睡觉,打豆豆,当然也会和你打个招呼
接下来,让我们来优化下这段代码。 我们不难发现,每只企鹅都会执行相同的动作 “睡觉”,所有的企鹅都会“吃饭”,但是吃的食物可能不相同。这样,我们可以抽象一个“企鹅”类,这批企鹅都会吃饭和睡觉,于是:
public abstract class Penguin { public void DoSomethingOnDay(string name,int age) { SayHi(name, age); Eat(); Sleep(); } public void SayHi(string Name, int Age) { Console.WriteLine($"我叫:{Name},我{Age}岁了,我每天要做的事是:"); } void Sleep() { Console.WriteLine("睡觉...."); } public abstract void Eat(); }
于是我们的三只企鹅又变成了这样:
public class PenguinDaPang : Penguin { public override void Eat() { Console.WriteLine("我在吃金枪鱼"); } } public class PenguinErHu : Penguin { public override void Eat() { Console.WriteLine("我在吃三文鱼"); } } public class PenguinDouDou : Penguin { public override void Eat() { Console.WriteLine("我在吃比目鱼"); } }
来走一波~
企鹅抽象类中,DoSomethingOnDay就是模板方法,它封装了一系列算法步骤,做到了代码的复用,对有变化的方法(例如Eat),延迟到子类去实现来适应不同的要求。
但是问题又来了,我们的企鹅还有一件事没有做,“打豆豆”。这件事并不是每一只企鹅都做,但是它又应该在模板方法中(因为大多说企鹅都会做),这样我们应该怎么处理呢?
模板方法给我们提供了一个方法叫做“钩子方法”,让我们先来看下修改后的代码。
public abstract class Penguin { public void DoSomethingOnDay(string name, int age) { SayHi(name, age); Eat(); Sleep(); if (IsBeat()) BeatDoudou(); } public void SayHi(string Name, int Age) { Console.WriteLine($"我叫:{Name},我{Age}岁了,我每天要做的事是:"); } void Sleep() { Console.WriteLine("睡觉...."); } public abstract void Eat(); public abstract void BeatDoudou(); //敲打豆豆 public virtual bool IsBeat() //钩子方法 { return true; } } public class PenguinDaPang : Penguin { public override void BeatDoudou() { Console.WriteLine("我在用树枝敲打豆豆"); } public override void Eat() { Console.WriteLine("我在吃金枪鱼"); } } public class PenguinErHu : Penguin { public override void BeatDoudou() { Console.WriteLine("我在用雪球敲打豆豆"); } public override void Eat() { Console.WriteLine("我在吃三文鱼"); } } public class PenguinDouDou : Penguin { public override bool IsBeat() //子类重写钩子方法 { if (typeof(PenguinDouDou).Name == "PenguinDouDou") return false; else return true; } public override void BeatDoudou() { Console.WriteLine("我特么就是豆豆"); } public override void Eat() { Console.WriteLine("我在吃比目鱼"); } }
钩子方法是在超类中的一个默认实现,我们这里的IsBeat()方法,默认是返回true的。子类中的"豆豆"是不会打自己的,所以,我们可以重写IsBeat()方法,当类是 "豆豆"类的时候,就返回一个false,那样就不再执行 "打豆豆这个操作"。这就是钩子方法的作用,用来控制超类中是否要执行一些算法步骤,默认有实现,也可以由子类来重写它。
,跑起来~~~
emmmmmm,现在我们知道了,钩子方法是高层类中一个默认实现的方法,它的作用是控制某些方法(算法步骤)是否执行,钩子方法也可以由子类去覆盖重写。
总结:
这就是一个简单的模版方法模式的例子了,现在让我们来总结下
1、“模板方法”(DoSomethingOnday)定义了算法的步骤,并且将有些步骤地实现延迟到了子类;
2、模板方法提供了代码重用的技巧
3、模版方法的抽象类(超类)定义具体方法,抽象方法和钩子方法(虚方法),抽象方法由子类去实现,钩子方法可以由子类选择是否去覆盖它。