1、引言
说到模板,我们很容易想到生活中的例子,像“简历模板”、“论文模板”、“竞选模板”等。而这些模板有一个共同的特征就是,它们有一个约定俗成的格式,我们能可以拿着这些模板按照我们自己的实际情况来填写里面的内容。像“简历模板”就是个十分常见的例子,我们在简历模板上只需要根据内容填写自己的实际情况就完成了自己的简历。在设计模式中,模板方法模式中所说的模板与生活中的模板也十分相似,下面让我们一起来了解下。
2、模板方法模式详细介绍
2.1、模板方法模式的定义
模板方法模式(TemplateMethod Pattern)
定义一个操作中的算法的骨架,而将一些算法步骤延申到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- 理解
在一个抽象类中定义一个操作中的算法骨架(对应于生活中的大家下载的模板),而将一些步骤延迟到子类中去实现(对应于我们根据自己的情况向模板填充内容)。模板方法使得子类可以不改变一个算法的结构前提下,重新定义算法的某些特定步骤,模板方法模式把不变行为搬到超类中,从而去除了子类中的重复代码。
2.2、模板方法模式的结构
下面是模板方法模式的类图,我们一起来看看!
通过上图我们不难发现,在模板方法模式中只有两个角色:
- 抽象模板角色:定义了一个或多个抽象操作,以便让子类实现,这些抽象操作称为基本操作。
- 具体模板角色:实现父类所定义的一个或多个抽象方法。每个AbsrtactClass可以由任意多个ConcreteClass与之对应,而每个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的实现步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
2.3、模板方法模式的类图实现
详细代码如下:
模板抽象类:
/// <summary>
/// 抽象类
/// </summary>
public abstract class AbstractClass
{
/// <summary>
/// 要放到子类中实现的一些抽象行为
/// </summary>
public abstract void PrimitiveOperation1();
public abstract void PrimitiveOperation2();
/// <summary>
/// 模板方法,给出了逻辑的骨架,
/// 而逻辑的组成是一些相应的抽象操作,他们都推迟到子类中实现
/// </summary>
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
Console.WriteLine("父类模板");
}
}
具体类:
/// <summary>
/// 具体类A
/// </summary>
public class ConcreteClassA : AbstractClass
{
/// <summary>
/// 具体行为
/// </summary>
public override void PrimitiveOperation1()
{
Console.WriteLine("子类A,方法1实现");
}
/// <summary>
/// 具体行为
/// </summary>
public override void PrimitiveOperation2()
{
Console.WriteLine("子类A,方法2实现");
}
}
public class ConcreteClassB : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("子类b,方法1实现");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("子类B,方法2实现");
}
}
测试一下:
//将子类的变量的声明改成了父类,利用多态实现代码的复用
AbstractClass c;
c = new ConcreteClassA();
c.TemplateMethod();
c = new ConcreteClassB();
c.TemplateMethod();
测试结果:
子类A,方法1实现
子类A,方法2实现
父类模板
子类b,方法1实现
子类B,方法2实现
父类模板
2.4、C#举例
- 情景设定:
炒蔬菜的做法:倒油——油热——倒入蔬菜——翻炒
蔬菜抽象类:
/// <summary>
/// 蔬菜抽象类
/// <summary>
public abstract class Vegetabel
{
// 模板方法,不要把模版方法定义为Virtual或abstract方法,
//避免被子类重写,防止更改流程的执行顺序
public void CookVegetabel()
{
Console.WriteLine("抄蔬菜的一般做法");
this.pourOil();
this.HeatOil();
this.pourVegetable();
this.stir_fry();
}
// 第一步倒油
public void pourOil()
{
Console.WriteLine("倒油");
}
// 把油烧热
public void HeatOil()
{
Console.WriteLine("把油烧热");
}
// 油热了之后倒蔬菜下去,具体哪种蔬菜由子类决定
public abstract void pourVegetable();
// 开发翻炒蔬菜
public void stir_fry()
{
Console.WriteLine("翻炒");
}
}
具体类:
// 菠菜
public class Spinach : Vegetabel
{
public override void pourVegetable()
{
Console.WriteLine("倒菠菜进锅中");
}
}
// 大白菜
public class ChineseCabbage : Vegetabel
{
public override void pourVegetable()
{
Console.WriteLine("倒大白菜进锅中");
}
}
测试一下:
// 创建一个菠菜实例并调用模板方法
Spinach spinach = new Spinach();
spinach.CookVegetabel();
测试结果:
抄蔬菜的一般做法
倒油
把油烧热
倒菠菜进锅中
翻炒
3、模板方法模式的优缺点
下面我们来看看模板方法模式的优缺点:
优点
- 实现了代码复用
- 能够灵活应对子步骤的变化,符合开放-封闭原则
缺点
- 因为引入了一个抽象类,如果具体实现过多的话,需要用户或开发人员需要花更多的时间去理清类之间的关系。
4、应用情景
下面我们来看看模板方法模式的使用情景:
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复;
- 控制子类的扩展。
5、 应用举例(unity)
模板类:
using UnityEngine;
using UnityEngine.UI;
using System;
public class Template {
protected Text mBtnText;
protected Text mText;
protected Image mImage;
protected Action Callback;
protected Button button;
public Template() { }
protected void Init(Transform transform)
{
mBtnText = transform.Find("Button/Text").GetComponent<Text>();
mText = transform.Find("Text").GetComponent<Text>();
mImage = transform.Find("Image").GetComponent<Image>();
button = transform.Find("Button").GetComponent<Button>();
button.onClick.RemoveAllListeners();
button.onClick.AddListener(OnClick);
}
public void OnClick()
{
Debug.Log("点击了按钮:" + mBtnText.text);
if (Callback != null)
Callback();
}
protected virtual void Method1()
{
}
public void TemplateMethod(Transform transform)
{
Init(transform);
Method1();
}
}
具体类A:
using UnityEngine;
public class ConcreteTemplateA : Template {
protected override void Method1()
{
base.Method1();
mText.text = "模板方法A";
mBtnText.text = "模板方法A按钮";
mImage.color = Color.green;
Callback = OnClickA;
}
private void OnClickA()
{
Debug.Log("模板方法A的按钮事件");
}
}
具体类B:
using UnityEngine;
public class ConcreteTemplateB : Template
{
protected override void Method1()
{
mText.text = "模板方法B";
mBtnText.text = "模板方法B按钮";
mImage.color = Color.yellow;
Callback = null;
Callback = OnClickB;
}
private void OnClickB()
{
Debug.Log("模板方法B的按钮事件");
}
}
测试一下:
using UnityEngine;
public class Test : MonoBehaviour {
void OnGUI()
{
if (GUI.Button(new Rect(100, 100, 120, 50), "按钮A"))
{
Template a = new ConcreteTemplateA();
a.TemplateMethod(this.transform);
}
if (GUI.Button(new Rect(100, 200, 120, 50), "按钮B"))
{
Template b = new ConcreteTemplateB();
b.TemplateMethod(this.transform);
}
}
}
测试脚本挂在Canvas上
运先点击按钮A或B,再点击右边白色按钮。行结果如下:
6、总结
到这里,模板方法模式的介绍就结束了,模板方法模式在抽象类中定义了算法的实现步骤,将这些步骤的实现延迟到具体子类中去实现,从而使所有子类复用了父类的代码,所以模板方法模式是基于继承的一种实现代码复用的技术。
7、unity工程下载
在文章的最后我们给出上述例子的工程下载链接,方便朋友们探讨交流!本次演示版本Unity5.6.3f1(64-bit),VS2015需要下载的朋友,请点击此处下载。
The End
好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!
喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!
相关阅读
C# 23种设计模式(unity演示)