用三种语言实现模板方法模式

领会其心,不拘一法。

模板方法模式,顾名思义,就是一个方法里有一些固定的模板部分,有一些可变的部分。模板方法模式的实质是,将流程中的固定和可变部分分离。

本文用三种语言来实现模板方法模式。

实现模板方法模式

Java

Java 实现模板方法模式的标准套路是:

  • 定义一个接口;
  • 定义一个抽象类,实现接口的方法,但是实现方法流程里留有一个未实现的部分,方法签名为 abstract;
  • 定义一个子类,实现抽象类中的 abstract 方法。

代码如下:

package zzz.study.pattern.tempatemethod;

import org.junit.Test;

/**
 * TemplateMethodTest:
 * created by qin.shu 2023/11/4
 */
public class TemplateMethodTest {

    @Test
    public void testCook() {
        Cook cookTomato = new CookTomato();
        cookTomato.doCook();

        Cook cookMeat = new CookMeat();
        cookMeat.doCook();
    }
}


interface Cook {
    void doCook();
}

abstract class AbstractCook implements Cook {

    public void doCook() {
        wash();
        pour_oil();
        cook();
        chuguozhuangpan();
    }

    abstract void cook();

    private void wash() {
        System.out.println("wash");
    }

    private void pour_oil() {
        System.out.println("pour_oil");
    }

    private void chuguozhuangpan() {
        System.out.println("chuguozhuangpan");
    }
}

class CookTomato extends AbstractCook {

    @Override
    void cook() {
        System.out.println("cook tomato");
    }
}

class CookMeat extends AbstractCook {

    @Override
    void cook() {
        System.out.println("cook meat");
    }
}

Go

接着,咱们用 Go 来模拟上面这段程序。

不过 Go 是没有类继承的语言特性的。Go 的接口“实现”即是基于行为:实现了接口的所有方法,就是实现了接口,无需特意用 implements 关键字表明。

Go 代码如下(这个例子来自网上示例):

package main

import (
    "fmt"
)

type Cooker interface {
    wash()
    pour_oil()
    cook()
    chuguozhuangpan()
}

// 类似于一个抽象类
type CookFlow struct {
}

func (CookFlow) wash() {
    fmt.Println("wash")
}

func (CookFlow) pour_oil() {
    fmt.Println("pour_oil")
}

// 做菜,交给具体的子类实现
func (CookFlow) cooke() {
}

func (CookFlow) chuguozhuangpan() {
    fmt.Println("chuguozhuangpan")
}


// 封装具体步骤
func doCook(cook Cooker) {
    cook.wash()
    cook.pour_oil()
    cook.cook()
    cook.chuguozhuangpan()
}

type Tomato struct {
    CookFlow
}

func (t *Tomato) cook() {
    fmt.Println("cook tomato")
}

type Meat  struct {
    CookFlow
}

func (m *Meat) cook() {
    fmt.Println("cook meat")
}

func main() {

    tomato := &Tomato{}
    doCook(tomato)

    meat := &Meat{}
    doCook(meat)

}

可以发现,Go 没有类继承,所以模板方法实现是放在函数里的。通过在函数参数中传入子类来实现。

对于 Go 这样的非对象语言来说,用对象语言思考,总会有点“拿着锤子把所有东西都看成钉子”的感觉。咱们现在来褪下“对象的外衣”,直视“模板方法模式的本体”,用函数式编程来实现。

可以看到,算法无非是一个编排,固定一部分,让另一部分可变。

实际上,我只消写一个模板方法实现,把可变部分作为函数参数传入即可。

package main

import "fmt"

func cook(doCook func()) {
    wash()
    pour_oil()
    doCook()
    chuguozhuangpan()
}

func wash() {
    fmt.Println("wash")
}

func pour_oil() {
    fmt.Println("pour_oil")
}

func chuguozhuangpan() {
    fmt.Println("chuguozhuangpan")
}

func main() {
    cook(func() {
        fmt.Println("cook tomato")
    })

    cook(func() {
        fmt.Println("cook meat")
    })

}


Python

流程,本质上就是函数的编排。咱们可以写得更通用些。

python 对函数式编程支持更好,编写起来更好。

如下代码所示: 将一个函数列表传入,遍历并调用,就实现了一个流程编排能力。然后把具体函数传入即可。是不是超简单?

def do_cook(cook_funcs):
    for func in cook_funcs:
        func()


def wash():
    print('wash')

def pour_oil():
    print('pour oil')


def cook_tomato():
    print('cook tomato')

def cook_meat():
    print('cook meat')

def pot():
    print('pot')


if __name__ == '__main__':

    do_cook([wash, pour_oil, cook_tomato, pot])
    do_cook([wash, pour_oil, cook_meat, pot])

还可以用函数 curry 来实现。所谓 curry, 就像高中所学的多元函数,传入一个参数,就可以得到另一个函数。比如 f(x,y) = x + y ,将 x = 1 传入,就得到了函数 f(y) = 1 + y。咱们用 curry 来实现模板方法模式。

def do_cook_by_curry(wash, pour, pot):
    def after(cook):
        wash()
        pour()
        cook()
        pot()
    return after

if __name__ == '__main__':

    print('template method pattern using func curry')
    cook_template = do_cook_by_curry(wash, pour_oil, pot)
    cook_template(cook_tomato)
    cook_template(cook_meat)

注意到:这里 do_cook_by_curry 函数的返回结果是一个函数,这个函数会传入一个可变的函数作为参数来调用。这样就实现了模板方法模式。是不是感觉很“犀利” ?

函数式编程与对象编程

函数式编程与对象编程都属于一种编程范式。那么,这两者各有什么优劣呢 ?

  • 函数式编程:基于不可变原则,短小精悍,通过函数组合能够快速建立强大的能力;
  • 对象编程:将状态和行为统一在对象里,其思想贴合事物的整体性。

对象编程,适合管理具有大量状态的工程(比如基于 DDD),适合于包装,本身是一种工程手段;函数式编程,适合实现强大的功能,更加纯粹。

对于很多 Java 工程来说,其实很多组件都是单例。数据都在数据对象里。如果领域比较复杂,对象和行为比较复杂,需要用 DDD 的思想来包装状态和行为,则用对象编程比较适合,否则不如直接用函数式编程,反而更加简洁。

函数式编程 + 泛型编程结合,是一种强大的编程技艺。若能娴熟掌握,可以甩同龄人一条街。

小结

本文用三种编程语言来实现模板方法模式。其主旨在于,用不同的思想和视角去看待同一件事情。这种方式可以开阔技术视角,不局限于某一种编程语言和平台。

当我们领会模式的思想和本质后,其实现手段和具体招式就可以随心变化。

领会其心,不拘一法。

参考资料

posted @ 2023-11-05 07:04  琴水玉  阅读(40)  评论(0编辑  收藏  举报