【设计模式】字节面试:请用一个故事解释模板方法模式!

76 阅读4分钟

你是否在编程中遇到过这样的场景:一段功能逻辑中大部分步骤都是相同的,但有一小部分会因为不同的需求而变动?而在每次变动时你都不得不复制大部分代码,重复编程?如果是的话,让我向你介绍一个神奇的设计模式:模板方法模式

什么是模板方法模式?

模板方法模式是一种行为设计模式,它定义了一个算法的骨架并把一些步骤延迟到子类中,使得子类可以不改变算法的结构就可以改变一些特定的步骤。这就好比你在烹饪时,无论你做什么菜,基本的步骤是一样的:洗菜、切菜、烹饪、摆盘,但在每个步骤中,你可以根据菜品不同,选择不同的操作。

例子:制作咖啡和茶

下面我们就用golang举例说明模板方法模式如何在实际中使用。这是一个制作咖啡和茶的例子:

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()
}

// Coffee structure
type Coffee struct {
    Beverage
}

func (c *Coffee) Brew() {
    fmt.Println("Dripping coffee through filter")
}

func (c *Coffee) AddCondiments() {
    fmt.Println("Adding sugar and milk")
}

// Tea structure
type Tea struct {
    Beverage
}

func (t *Tea) Brew() {
    fmt.Println("Steeping the tea")
}

func (t *Tea) AddCondiments() {
    fmt.Println("Adding lemon")
}

在这个例子中,Beverage类型定义了一种制作饮料的标准流程,也就是我们的模板方法PrepareRecipe:BoilWater,Brew,PourInCup和AddCondiments。Brew和AddCondiments这两个方法在Beverage中没有具体实现,它们的具体实现由子类根据自己的特性去完成。

什么场景可以使用模板方法模式?

简单来说,模板方法模式主要适用于以下场景:

  1. 一次性实现一个算法的不变部分:在很多情况下,我们可能会遇到一些功能,它们在大部份的逻辑是相同的,但是一小部分会随着场景的变化而变化。这个时候就可以使用模板方法模式,将不变的部分定义在父类中,将可变的部分延迟到具体子类中实现。
  2. 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复:首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个"模板方法"来调用这些新的操作。
  3. 控制子类的扩展:模板方法只在特定点调用“hook"方法,这样只在这些点才能够进行扩展。

比如在编程领域,一个数据库访问的类,通常都有类似的操作流程,比如创建链接、设置命令、执行命令、关闭链接等,而只有设置命令的地方,会因为具体命令的不同而发生变化,这就是个模板方法模式的例子。

模板方法模式的好处

模板方法模式的好处就多了:

  1. 提高代码复用:模板方法模式通过在抽象类中定义一个算法的框架,将一些具体步骤的实现延迟到子类中,从而实现代码复用。避免了大量重复的代码,使程序更易维护
  2. 增强了代码的可读性:通过明确的将变化和不变分离,有助于我们更好的理解这个算法框架,提高了代码的可读性。
  3. 提供了一种代码组织框架:当我们需要构建大量的类似但却又有些许不同的功能时,模板方法模式可以为我们提供一个有效的组织架构。使得我们的代码更易扩展,更易维护。
  4. 封装了代码变化:把变化的代码封装到了子类中,如果未来要改变部分算法,那么只需要改变对应的子类代码就可以了,不会影响其他部分。
  5. 控制操作的流程:父类通过模板方法定义了整个流程的骨架,一般不允许子类改变。保证了操作流程的正确性。

总的来说,模板方法模式能够帮助我们高效地处理某些重复、复杂的问题,降低了代码冗余,提高了代码的整洁度和可维护性。

如果上面的内容对你有帮助,请点赞收藏哦,我会分享更多的经验~