Contents

闲聊设计模式 | 模板方法模式

一、前言

Template Pattern 模板方法 来自 Wiki 百科的介绍:

模板方法模型是一种行为设计模型。模板方法是一个定义在父类别的方法,在模板方法中会呼叫多个定义在父类别的其他方法,而这些方法有可能只是抽象方法并没有实作,模板方法仅决定这些抽象方法的执行顺序,这些抽象方法的实作由子类别负责,并且子类别不允许覆写模板方法。

模板方法是属于设计模式的行为型模式

模板方法模式按照我的白话文理解:

首先定一个“抽象类”,它有一个模板方法A,定义可能需要子类实现的方法B,C,D…,然后在A方法里面编排好了B,C,D等的位置,当实现类调用A的时候,会调用各自实现的B,C,D方法(某些方法有默认实现,子类可以不实现),从而在一样的大流程里面进行不一样的操作。

二、简单实例

故事背景

阳光明媚的一天,玩码部落来了一群腿长一米八的MM,它们来自台湾,杭州及北京,她们将介绍各自家乡是如何准备丰富的晚餐的。

2.1 Java版本

2.1.1 定义接口

1
2
3
4
5
6
7
8
public interface Dinner {

    /**
     * 晚餐
     */
    void doDinner();

}

定义一个接口,就一个无参的 void 方法。

2.1.2 定义抽象方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public abstract class AbstractDinner implements Dinner {

    protected String name;

    public AbstractDinner(String name) {
        this.name = name;
    }

    private void eat() {
        System.out.printf("%sMM说:开吃喽", name).println();
    }

    protected boolean foodEnough() {
        return true;
    }

    protected void doShopping() {
        System.out.println("门口小贩买菜");
    }

    protected abstract void beforeCooking();

    protected abstract String doCooking();

    protected abstract void afterCooking();

    @Override
    public void doDinner() {
        if (!foodEnough()) {
            doShopping();
        }

        beforeCooking();
        System.out.println(doCooking());
        afterCooking();

        eat();
    }

}

定义 AbstractDinner 实现接口,它自身有五个方法,默认实现的 foodEnoughdoShopping,以及抽象方法 beforeCookingdoCookingafterCooking

doDinner 编排了这些方法的流程或者说定义了各阶段的步骤。

2.1.3 定义实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public class BeijingDinner extends AbstractDinner {

    public BeijingDinner(String name) {
        super(name);
    }

    @Override
    protected void beforeCooking() {
        System.out.printf("%sMM 在洗菜切菜", name).println();
    }

    @Override
    protected String doCooking() {
        return name + "MM 在做" + name + "菜";
    }

    @Override
    protected void afterCooking() {
        System.out.printf("%sMM 让你去品尝", name).println();
    }

}

public class TaiwanDinner extends AbstractDinner {

    public TaiwanDinner(String name) {
        super(name);
    }

    @Override
    protected boolean foodEnough() {
        // 每次都买食物
        return false;
    }

    @Override
    protected void doShopping() {
        System.out.println("生鲜超市购买,一定要买茶叶蛋");
    }

    @Override
    protected void beforeCooking() {
        System.out.printf("%sMM 在洗菜切菜", name).println();
    }

    @Override
    protected String doCooking() {
        return name + "MM 在做" + name + "菜";
    }

    @Override
    protected void afterCooking() {
        System.out.printf("%sMM 让你去品尝", name).println();
    }

}

public class HangzhouDinner extends AbstractDinner {

    public HangzhouDinner(String name) {
        super(name);
    }

    @Override
    protected boolean foodEnough() {
        // 每次都买食物
        return false;
    }

    @Override
    protected void beforeCooking() {
        System.out.printf("%sMM 在洗菜切菜", name).println();
    }

    @Override
    protected String doCooking() {
        return name + "MM 在做" + name + "菜";
    }

    @Override
    protected void afterCooking() {
        System.out.printf("%sMM 让你去品尝", name).println();
    }

}

定义了三个实现类,都实现了3个抽象方法。另外 TaiwanDinner 重写了另外两个方法,HangzhouDinner 只重写了 foodEnough

2.1.4 运行例子

代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class DinnerDemo {

    public static void main(String[] args) {
        System.out.println("---准备台湾餐---");
        Dinner dinner1 = new TaiwanDinner();
        dinner1.doDinner();
        System.out.println("---准备杭州餐---");
        Dinner dinner2 = new HangzhouDinner();
        dinner2.doDinner();
        System.out.println("---准备北京餐---");
        Dinner dinner3 = new BeijingDinner();
        dinner3.doDinner();
    }

}

输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
---准备台湾餐---
生鲜超市购买,一定要买茶叶蛋
台湾MM 在洗菜切菜
台湾MM 在做台湾菜
台湾MM 让你去品尝
台湾MM说:开吃喽
---准备杭州餐---
门口小贩买菜
杭州MM 在洗菜切菜
杭州MM 在做杭州菜
杭州MM 让你去品尝
杭州MM说:开吃喽
---准备北京餐---
北京MM 在洗菜切菜
北京MM 在做北京菜
北京MM 让你去品尝
北京MM说:开吃喽

2.2 Golang 版本

声明下:在 golang 中,由于不存在抽象类和真正的继承,所以只能通过一个基础类来充当抽象类,子类通过组合基础类来实现通用方法的继承。

2.2.1 定义接口

1
2
3
type Dinner interface {
	DoDinner()
}

2.2.2 定义抽象类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
type AbstractCooking struct {
	foodEnough    func() bool
	doShopping    func()
	beforeCooking func()
	doCooking     func() string
	afterCooking  func()
	Name          string
}

func (d *AbstractCooking) DoDinner() {
	if !d.foodEnough() {
		d.doShopping()
	}
	d.beforeCooking()
	fmt.Println(d.doCooking())
	d.afterCooking()
	d.eat()
}

func (d *AbstractCooking) eat() {
	fmt.Println(fmt.Sprintf("%sMM说:开吃喽", d.Name))
}

这里和 Java 不一样的地方是 go 的结构体可以拥有 func() 属性(也可以拥有接口属性)。

实现 Dinner 的方法 DoDinner , 编排了一系列的方法。

2.2.3 定义实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
type HZDinner struct {
	AbstractCooking
}

func NewHZDinner(name string) *HZDinner {
	c := new(HZDinner)
	c.Name = name
	// 选择实现的
	c.AbstractCooking.foodEnough = c.foodEnough
	c.AbstractCooking.doShopping = doShopping
	// 必须实现的
	c.AbstractCooking.beforeCooking = c.beforeCooking
	c.AbstractCooking.doCooking = c.doCooking
	c.AbstractCooking.afterCooking = c.afterCooking
	return c
}

func (c *HZDinner) foodEnough() bool {
	return false
}

func (c *HZDinner) beforeCooking() {
	println(fmt.Printf("%sMM 在洗菜切菜", c.Name))
}

func (c *HZDinner) doCooking() string {
	return fmt.Sprintf("%sMM 在做%s菜", c.Name, c.Name)
}

func (c *HZDinner) afterCooking() {
	println(fmt.Printf("%sMM 让你去品尝", c.Name))
}

type TWDinner struct {
	AbstractCooking
}

func NewTWDinner(name string) *TWDinner {
	c := new(TWDinner)
	c.Name = name
	// 选择实现的
	c.AbstractCooking.foodEnough = c.foodEnough
	c.AbstractCooking.doShopping = c.doShopping
	// 必须实现的
	c.AbstractCooking.beforeCooking = c.beforeCooking
	c.AbstractCooking.doCooking = c.doCooking
	c.AbstractCooking.afterCooking = c.afterCooking
	return c
}

func (c *TWDinner) foodEnough() bool {
	return false
}

func (c *TWDinner) doShopping() {
	fmt.Println("生鲜超市购买,一定要买茶叶蛋")
}

func (c *TWDinner) beforeCooking() {
	println(fmt.Printf("%sMM 在洗菜切菜", c.Name))
}

func (c *TWDinner) doCooking() string {
	return fmt.Sprintf("%sMM 在做%s菜", c.Name, c.Name)
}

func (c *TWDinner) afterCooking() {
	println(fmt.Printf("%sMM 让你去品尝", c.Name))
}

type BJDinner struct {
	AbstractCooking
}

func NewBJDinner(name string) *BJDinner {
	c := new(BJDinner)
	c.Name = name
	// 选择实现的
	c.AbstractCooking.foodEnough = foodEnough
	c.AbstractCooking.doShopping = doShopping
	// 必须实现的
	c.AbstractCooking.beforeCooking = c.beforeCooking
	c.AbstractCooking.doCooking = c.doCooking
	c.AbstractCooking.afterCooking = c.afterCooking
	return c
}

func (c *BJDinner) beforeCooking() {
	println(fmt.Printf("%sMM 在洗菜切菜", c.Name))
}

func (c *BJDinner) doCooking() string {
	return fmt.Sprintf("%sMM 在做%s菜", c.Name, c.Name)
}

func (c *BJDinner) afterCooking() {
	println(fmt.Printf("%sMM 让你去品尝", c.Name))
}

2.2.4 定义默认实现方法

1
2
3
4
5
6
7
func foodEnough() bool {
	return true
}

func doShopping() {
	fmt.Println("门口小贩买菜")
}

为什么有独立的默认方法,因为 struct 里面定义了 foodEnough() booldoShopping() 两个方法,go 里面是不能重名的,因此不能再写属于 AbstractCooking 的方法。

1
2
3
func (d *AbstractCooking) foodEnough() bool {
	return true
}

这个方法如何写了,去掉了 struct 的 foodEnough() bool,那么创建实现类的时候就没办法 c.AbstractCooking.foodEnough = c.foodEnough 进行 func() 赋值,从而 d.foodEnough() 会一直调用 AbstractCooking 下的 foodEnough(),实现类没办法自定义实现了。

2.2.5 运行例子

代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func TestTemplate1(t *testing.T) {
	fmt.Println("---准备台湾餐---")
	d1 := NewTWDinner("台湾")
	d1.DoDinner()
	fmt.Println("---准备杭州餐---")
	d2 := NewHZDinner("杭州")
	d2.DoDinner()
	fmt.Println("---准备北京餐---")
	d3 := NewBJDinner("北京")
	d3.DoDinner()
}

输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
---准备台湾餐---
生鲜超市购买,一定要买茶叶蛋
台湾MM 在洗菜切菜
台湾MM 在做台湾菜
台湾MM 让你去品尝
台湾MM说:开吃喽
---准备杭州餐---
门口小贩买菜
杭州MM 在洗菜切菜
杭州MM 在做杭州菜
杭州MM 让你去品尝
杭州MM说:开吃喽
---准备北京餐---
北京MM 在洗菜切菜
北京MM 在做北京菜
北京MM 让你去品尝
北京MM说:开吃喽

2.3 Golang版本2

上面例子是在 struct 中定义,相当于是抽象方法的意思,而这版本把那部分方法都定义到了接口。

2.3.1 定义接口

1
2
3
4
5
6
7
type Dinner2 interface {
	foodEnough() bool
	doShopping()
	beforeCooking()
	doCooking() string
	afterCooking()
}

2.3.2 定义抽象类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
type AbstractDinner struct {
}

func (AbstractDinner) foodEnough() bool {
	return true
}

func (AbstractDinner) doShopping() {
	fmt.Println("门口小贩买菜")
}

func (AbstractDinner) beforeCooking() {
}

func (AbstractDinner) doCooking() string {
	return ""
}

func (AbstractDinner) afterCooking() {
}

实现 Dinner2 接口,和下面等价

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
type AbstractDinner struct {
	Dinner2
}

func (AbstractDinner) foodEnough() bool {
	return true
}

func (AbstractDinner) doShopping() {
	fmt.Println("门口小贩买菜")
}

2.3.3 定义实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
type HangzhouDinner struct {
	AbstractDinner
}

func NewHangzhouDinner(name string) Dinner2 {
	return &HangzhouDinner{
		AbstractDinner{
			Name: name,
		},
	}
}

func (d *HangzhouDinner) foodEnough() bool {
	return false
}

func (d *HangzhouDinner) beforeCooking() {
	fmt.Println(fmt.Sprintf("%sMM 在洗菜切菜", d.Name))
}

func (d *HangzhouDinner) doCooking() string {
	return fmt.Sprintf("%sMM 在做%s菜", d.Name, d.Name)
}

func (d *HangzhouDinner) afterCooking() {
	fmt.Println(fmt.Sprintf("%sMM 让你去品尝", d.Name))
}

type BeijingDinner struct {
	AbstractDinner
}

func NewBeijingDinner(name string) Dinner2 {
	return &BeijingDinner{
		AbstractDinner{
			Name: name,
		},
	}
}

func (d *BeijingDinner) beforeCooking() {
	fmt.Println(fmt.Sprintf("%sMM 在洗菜切菜", d.Name))
}

func (d *BeijingDinner) doCooking() string {
	return fmt.Sprintf("%sMM 在做%s菜", d.Name, d.Name)
}

func (d *BeijingDinner) afterCooking() {
	fmt.Println(fmt.Sprintf("%sMM 让你去品尝", d.Name))
}

type TaiwanDinner struct {
	AbstractDinner
}

func NewTaiwanDinner(name string) Dinner2 {
	return &TaiwanDinner{
		AbstractDinner{
			Name: name,
		},
	}
}

func (d *TaiwanDinner) foodEnough() bool {
	return false
}

func (d *TaiwanDinner) doShopping() {
	fmt.Println("生鲜超市购买,一定要买茶叶蛋")
}

func (d *TaiwanDinner) beforeCooking() {
	fmt.Println(fmt.Sprintf("%sMM 在洗菜切菜", d.Name))
}

func (d *TaiwanDinner) doCooking() string {
	return fmt.Sprintf("%sMM 在做%s菜", d.Name, d.Name)
}

func (d *TaiwanDinner) afterCooking() {
	fmt.Println(fmt.Sprintf("%sMM 让你去品尝", d.Name))
}

2.3.4 定义模板方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func DoDinner(d Dinner2) {
	if !d.foodEnough() {
		d.doShopping()
	}

	d.beforeCooking()
	fmt.Println(d.doCooking())
	d.afterCooking()
  d.eat()
}

func (ad AbstractDinner) eat() {
	fmt.Println(fmt.Sprintf("%sMM说:开吃喽", ad.Name))
}

如果想把抽象方法放到结构体上,也可以如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
type Dinner2 interface {
	...
	DoDinner(d Dinner2)
}

func (ad AbstractDinner) DoDinner(d Dinner2) {
	if !d.foodEnough() {
		d.doShopping()
	}

	d.beforeCooking()
	fmt.Println(d.doCooking())
	d.afterCooking()
	ad.eat()
}

func (ad AbstractDinner) eat() {
	fmt.Println(fmt.Sprintf("%sMM说:开吃喽", ad.Name))
}

到时候调用的话就 DoDinner 改成 d1 := NewTaiwanDinner("台湾") d1.DoDinner(d1)

2.3.5 运行例子

代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func TestTemplate2(t *testing.T) {
	fmt.Println("---准备台湾餐---")
	d1 := NewTaiwanDinner("台湾")
	DoDinner(d1)
	fmt.Println("---准备杭州餐---")
	d2 := NewHangzhouDinner("杭州")
	DoDinner(d2)
	fmt.Println("---准备北京餐---")
	d3 := NewTaiwanDinner("北京")
	DoDinner(d3)
}

输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
---准备台湾餐---
生鲜超市购买,一定要买茶叶蛋
台湾MM 在洗菜切菜
台湾MM 在做台湾菜
台湾MM 让你去品尝
台湾MM说:开吃喽
---准备杭州餐---
门口小贩买菜
杭州MM 在洗菜切菜
杭州MM 在做杭州菜
杭州MM 让你去品尝
杭州MM说:开吃喽
---准备北京餐---
生鲜超市购买,一定要买茶叶蛋
北京MM 在洗菜切菜
北京MM 在做北京菜
北京MM 让你去品尝
北京MM说:开吃喽

2.4、例子说明

三、开源框架使用场景

列举某几个框架,供大家参考

3.1 JDK AbstractList

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
  	...
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }  
    
  	...
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);
        boolean modified = false;
        for (E e : c) {
            add(index++, e);
            modified = true;
        }
        return modified;
    }  
  	...
}

实现类实现 add 的逻辑,如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
  	// ...
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
  	// ...
}        

3.1 spring 中的 事务管理器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
 
  // ...
	public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
		Object transaction = doGetTransaction();

		// Cache debug flag to avoid repeated checks.
		boolean debugEnabled = logger.isDebugEnabled();

		if (definition == null) {
			// Use defaults if no transaction definition given.
			definition = new DefaultTransactionDefinition();
		}

		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}

		// Check definition settings for new transaction.
		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
			}
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException ex) {
				resume(null, suspendedResources);
				throw ex;
			}
			catch (Error err) {
				resume(null, suspendedResources);
				throw err;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + definition);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
		}
	}
  // ...
    
	protected abstract void doBegin(Object transaction, TransactionDefinition definition)
			throws TransactionException;    
  
  ...
}

这里只拿 getTransactiondoBegin 举例,非常标准的写法 getTransaction 还用了 final 描述,表示子类不允许改变。

当我自定义事务管理器的时候,比如每次事务开启创建一个 traceId,效果如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class GongDaoDataSourceTransactionManager extends DataSourceTransactionManager implements
    ResourceTransactionManager, InitializingBean, EnvironmentAware {
  
  	// ...
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        String currentXid;
        if (XIDContext.getCurrent() != null && null != XIDContext.getCurrent().getId()) {
            currentXid = xidGenerator.getXID();
            XIDContext.childCurrent(new TransactionContent(currentXid, definition.getName()));
        } else {
            currentXid = xidGenerator.getXID();
            XIDContext.setXid(new TransactionContent(currentXid, definition.getName()));
        }

        if (null == HyjalTransactionFileAppender.contextHolder.get()) {
            String logFileName = HyjalTransactionFileAppender.makeLogFileName(new Date(),
                HyjalTransactionFileAppender.logId.get());
            HyjalTransactionFileAppender.contextHolder.set(logFileName);
        }

        try {
            if (null == XIDContext.getCurrentXid()) {
                XIDContext.setXid(xidGenerator.getXID());
            }
            super.doBegin(transaction, definition);
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("do begin Xid : {}", currentXid);
                HyjalTransactionLogger.log("do begin Xid : {}, obj : {}", currentXid, definition.getName());
            }
        }
    }
  	// ...
}  

欢迎各位补充更多的例子和场景

四、优势和劣势

4.1 优势

  • 对扩展开放,对修改关闭,符合“开闭原则”
    • 定义标准算法,子类可自定义扩展,把变性和不变性分离
    • 子类的扩展不会导致标准算法结构
  • 能够提高代码复用,公共部分易维护

4.2 劣势

  • 每一个实现类都需要定义自己的行为,如果复杂业务实现类会膨胀的比较多

五、参考

https://www.tutorialspoint.com/design_pattern/template_pattern.htm

https://zh.wikipedia.org/wiki/%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95