社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
在游戏服务端开发中,经常也需要使用到这个设计模式。如较为常用的开箱子操作,不同的箱子开启的每个步骤都不同,如获取多少装备,是否增加金币等。如果每个箱子都拥有自己单独开箱代码则代码变的混乱且不易于维护,用模板方法模式则可以让代码变的易于修改,具体箱子只维护自己实现部分的细节,对于通用的骨架则无需改变。然而golang没有真实意义上的“继承”概念,所以需要通过某些转换达到此目的。
————————————————————————————————————
class Program
{
static void Main(string[] args)
{
Parent p1 = new Child1();
p1.Exec();
Console.WriteLine("");
Parent p2 = new Child2();
p2.Exec();
Console.Read();
}
}
public class Parent
{
public virtual int Detail()
{
Console.WriteLine("parent step2");
return 0;
}
public virtual void Exec()
{
int i = Detail();
// 处理一些通用逻辑
Console.WriteLine("父类处理一些通用逻辑, 处理参数:" + i);
}
}
public class Child1 : Parent
{
public override int Detail()
{
Console.WriteLine("Child1 detail");
return 1;
}
public override void Exec()
{
base.Exec();
Console.WriteLine("父类通用逻辑处理完毕,child1处理自身逻辑");
}
}
public class Child2 : Parent
{
public override int Detail()
{
Console.WriteLine("Child2 detail");
return 2;
}
public override void Exec()
{
base.Exec();
Console.WriteLine("父类通用逻辑处理完毕,child2处理自身逻辑");
}
}
执行结果:
可以看出,子类通过调用自身的Exec,执行父类Exec,父类Exec又从Detail方法获取细节,细节又被子类重写。在基础骨架不变的情况下,只需增加子类并实现子类具体细节,就可以很方便的达到扩展的目的。
然而golang中没有实际意义上的继承、抽象函数等概念,若单纯只把父类嵌入子类,同C#一样写代码,则执行到父类的Exec函数时,会执行到父类的Detail函数,无法被子类重写,所以需要进行一定转换。
———————————————————————————————————
1)先定义通用接口与细节接口:
type CommonInterface interface {
Init()
Exec()
}
type DetailInterface interface {
Detail() int
}
2)父类与子类实现接口:
type Base struct {
DetailInterface
}
func (p *Base) Init(){
}
func (p *Base) Exec(){
i := p.Detail()
fmt.Println("父类处理一些通用逻辑,处理参数:", i)
}
type Child1 struct {
Base
}
func (c1 *Child1) Init(){
c1.DetailInterface = c1
}
func (c1 *Child1) Detail() int{
fmt.Println("子类1实现的具体细节")
return 1
}
func (c1 *Child1) Exec(){
c1.Base.Exec()
fmt.Println("子类1执行完毕")
}
此处,"父类"与子类实现通用接口,细节接口嵌入"父类",父类嵌入子类,子类在初始化时为细节接口赋值自身,这样在父类调用Exec时,走的是接口方法即具体子类的细节。
[注意,若此处直接在Base类中写一个Detail方法并将其加入common接口,调用时无法定位到子类具体实现]
3)执行与输出:
func main() {
var com CommonInterface = new(Child1)
com.Init()
com.Exec()
}
输出:
—————————————————————————————————————
type Base struct {
detailFunc func() int
}
func (p *Base) Init(){
}
func (p *Base) Exec(){
i := p.detailFunc()
fmt.Println("父类处理一些通用逻辑,处理参数:", i)
}
type Child1 struct {
Base
}
func (c1 *Child1) Init(){
c1.detailFunc = c1.Detail
}
func (c1 *Child1) Detail() int{
fmt.Println("子类1实现的具体细节")
return 1
}
func (c1 *Child1) Exec(){
c1.Base.Exec()
fmt.Println("子类1执行完毕")
}
执行与输出结果同上。
golang虽没有严格意义上的继承,但是成员变量是可以重写的,此处就包括"父类"的函数指针,通过重写函数指针定位到自身的细节处理函数,可以用这种曲线救国的方式间接实现多态。
自此代码讲解部分结束。后续部分为个人记忆用。
————————————————————————————————————————————
善用模板模式可以使代码更专注于细节,提高代码复用性
举例:项目中的箱子系统,不同的箱子可以开出装备/金币/钻石等,开出的装备等级又根据具体箱子决定。所以可以把“开组件”“开箱”作为模板中的两个方法,每个箱子实现自身细节。
代码:
初始化(加载配置,根据数据库载入数据,为细节接口赋值自身)
细节方法:
还有其他两种类型的箱子,也只需实现这两部分的细节即可。
(父类就不写了,和具体项目相关,但是拥有OpenGetResult方法)
(其中,父类的OpenGetResult就相当于Exec,需要调用到细节的OpenAccComp获取数据,进行一些通用的处理。)
总结:通过模版模式,我们可以把子类做为父类的模版,提取出公共的结构到父类,共享父类的代码。这样能消除代码结构重复的坏味。并且,简化了子类的功能,使之职责单一的为“父类”提供数据。
但是这两种golang的实现方式,细节部分都需要在初始化之类的某个地方赋值自身,感觉还是比较麻烦的,可能是go语言特性决定的吧,为了纯粹而舍弃了一些东西。
如果有更简单的实现方式或者有对于go语言设计思路的见解,欢迎讨论。
————————————————————————————————————————
尾声:看了一下居然半年没更新博客了,这半年发生的事也挺多,直到现在才闲到上班时间都能学习。其实在公司内网还是写了不少知识沉淀的,但是本着发到CSDN的文章都应该认真写的原则,还是很久都没更新了。也是为了写的东西大家都能看得懂不白写。前一段时间在看设计模式,说实在设计模式还是挺重要的,这两天抽空把设计模式的思维总结一下吧,也写一篇博客好了。
要多努力,走出舒适区。
Fighting
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!