Go语言的模板方法模式实现| 豆包MarsCode AI刷题

35 阅读3分钟

前面我们讨论了Go语言中在没有继承的情况下,代理模式是如何实现的。但是少了继承之后,受影响的除了代理模式,还有今天我们要讨论的模板方法模式,我们知道的是,模板方法模式要求的是,我们在父类中实现一个方法调用的模板/流程,而不具体实现这些方法,留待子类去实现这些方法,然后我们实例化对象之后,调用这个父类的方法,父类的方法就会按照他定义好的流程去调用子代的方法,由此提高代码的复用性和扩展性。而Go中没有继承这个概念,我们该如何去实现Go中的模板方法模式呢。

我们知道Go使用组合替代掉了继承,那么我们能使用组合来实现模板方法模式吗?

1. 模板方法模式的实现(基于组合)

我们在一个类中定义好结构,然后用另一个类去组合它,然后外部调用这个类的模板方法:

type Beverage struct{}
func (b *Beverage) BoilWater() {
	fmt.Println("Boiling water")
}
func (b *Beverage) Brew() {}
func (b *Beverage) PourInCup() {
	fmt.Println("Pouring into cup")
}
func (b *Beverage) AddCondiments() {}
func (b *Beverage) PrepareRecipe() {
	b.BoilWater()
	b.Brew()
	b.PourInCup()
	b.AddCondiments()
}
type Coffee struct {
	Beverage
}
func (c *Coffee) Brew() {
	fmt.Println("Dripping coffee through filter")
}
func (c *Coffee) AddCondiments() {
	fmt.Println("Adding sugar and milk")
}

我想实现的效果是调用对象的PrepareRecipe,然后RrepareRecipe调用Coffee的Brew方法,但是这样是不行的,毕竟组合不是继承,组合进去的对象,虽然我们可以用外部对象直接调用组合对象的方法,甚至也可以重写组合对象的方法,看起来就和继承一样,但是组合是不会使用外部对象定义的方法的。这也是为什么Go使用组合而不是继承的原因,继承破坏了一部分父类的封装,这导致我们在重写一些父类的方法的时候,必须清楚的明白这个方法在父类的作用,改编不是乱编,不考虑全面是会报莫名其妙的错的。

2. 模板方法模式(真)

言归正传,那么在Go中如何实现模板方法呢?

根据我找到的资料来看,可以定义一个模板类,在这个模板类中规定方法的框架,我们定义一个接口,这个接口定义了这个模板类需要用到的方法,然后我们实现这个接口,再用这个模板类包装我们的类。

我们将需要动态变化的类抽取出来变成一个接口,把方法的模板抽取出来作为一个类,然后Coffee实现这个接口,并将Coffee作为模板类的成员,如下:

type Beverage interface {
	Brew()

	AddCondiments()
}
type Drink struct {
	Beverage
}
func (b *Drink) BoilWater() {
	fmt.Println("Boiling water")
}
func (b *Drink) PourInCup() {
	fmt.Println("Pouring into cup")
}
func (b *Drink) PrepareRecipe() {
	b.BoilWater()
	b.Brew()
	b.PourInCup()
	b.AddCondiments()
}
type Coffee struct {
	Beverage
}
func (c *Coffee) Brew() {
	fmt.Println("Dripping coffee through filter")
}
func (c *Coffee) AddCondiments() {
	fmt.Println("Adding sugar and milk")
}
type Tea struct {
	Beverage
}
func (t *Tea) Brew() {
	fmt.Println("Steeping the tea")
}
func (t *Tea) AddCondiments() {
	fmt.Println("Adding lemon")
}

然后实例化的时候传入一下接口的实现:

	coffee := &Drink{&Coffee{}}
	tea := &Drink{&Tea{}}
	coffee.PrepareRecipe()
	tea.PrepareRecipe()

以上 我们就实现了Go的模板方法模式,不过,既然都把框架单独抽取出来了,如果类只需要一个模板的话,我们也可以直接用一个方法来实现这种的模板方法(也许):

func DrinkPrepareRecipe(b Beverage){
	fmt.Println("Boiling water")
	b.Brew()
	fmt.Println("Pouring into cup")
	b.AddCondiments()
}