目录

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

本文介绍设计模式之模板方法模式.

一、前言

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

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

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

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

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

二、简单实例

故事背景

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

2.1 Java版本

2.1.1 定义接口

1public interface Dinner {
2
3    /**
4     * 晚餐
5     */
6    void doDinner();
7
8}

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

2.1.2 定义抽象方法

 1public abstract class AbstractDinner implements Dinner {
 2
 3    protected String name;
 4
 5    public AbstractDinner(String name) {
 6        this.name = name;
 7    }
 8
 9    private void eat() {
10        System.out.printf("%sMM说:开吃喽", name).println();
11    }
12
13    protected boolean foodEnough() {
14        return true;
15    }
16
17    protected void doShopping() {
18        System.out.println("门口小贩买菜");
19    }
20
21    protected abstract void beforeCooking();
22
23    protected abstract String doCooking();
24
25    protected abstract void afterCooking();
26
27    @Override
28    public void doDinner() {
29        if (!foodEnough()) {
30            doShopping();
31        }
32
33        beforeCooking();
34        System.out.println(doCooking());
35        afterCooking();
36
37        eat();
38    }
39
40}

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

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

2.1.3 定义实现类

 1public class BeijingDinner extends AbstractDinner {
 2
 3    public BeijingDinner(String name) {
 4        super(name);
 5    }
 6
 7    @Override
 8    protected void beforeCooking() {
 9        System.out.printf("%sMM 在洗菜切菜", name).println();
10    }
11
12    @Override
13    protected String doCooking() {
14        return name + "MM 在做" + name + "菜";
15    }
16
17    @Override
18    protected void afterCooking() {
19        System.out.printf("%sMM 让你去品尝", name).println();
20    }
21
22}
23
24public class TaiwanDinner extends AbstractDinner {
25
26    public TaiwanDinner(String name) {
27        super(name);
28    }
29
30    @Override
31    protected boolean foodEnough() {
32        // 每次都买食物
33        return false;
34    }
35
36    @Override
37    protected void doShopping() {
38        System.out.println("生鲜超市购买,一定要买茶叶蛋");
39    }
40
41    @Override
42    protected void beforeCooking() {
43        System.out.printf("%sMM 在洗菜切菜", name).println();
44    }
45
46    @Override
47    protected String doCooking() {
48        return name + "MM 在做" + name + "菜";
49    }
50
51    @Override
52    protected void afterCooking() {
53        System.out.printf("%sMM 让你去品尝", name).println();
54    }
55
56}
57
58public class HangzhouDinner extends AbstractDinner {
59
60    public HangzhouDinner(String name) {
61        super(name);
62    }
63
64    @Override
65    protected boolean foodEnough() {
66        // 每次都买食物
67        return false;
68    }
69
70    @Override
71    protected void beforeCooking() {
72        System.out.printf("%sMM 在洗菜切菜", name).println();
73    }
74
75    @Override
76    protected String doCooking() {
77        return name + "MM 在做" + name + "菜";
78    }
79
80    @Override
81    protected void afterCooking() {
82        System.out.printf("%sMM 让你去品尝", name).println();
83    }
84
85}

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

2.1.4 运行例子

代码:

 1public class DinnerDemo {
 2
 3    public static void main(String[] args) {
 4        System.out.println("---准备台湾餐---");
 5        Dinner dinner1 = new TaiwanDinner();
 6        dinner1.doDinner();
 7        System.out.println("---准备杭州餐---");
 8        Dinner dinner2 = new HangzhouDinner();
 9        dinner2.doDinner();
10        System.out.println("---准备北京餐---");
11        Dinner dinner3 = new BeijingDinner();
12        dinner3.doDinner();
13    }
14
15}

输出结果:

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

2.2 Golang 版本

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

2.2.1 定义接口

1type Dinner interface {
2	DoDinner()
3}

2.2.2 定义抽象类

 1type AbstractCooking struct {
 2	foodEnough    func() bool
 3	doShopping    func()
 4	beforeCooking func()
 5	doCooking     func() string
 6	afterCooking  func()
 7	Name          string
 8}
 9
10func (d *AbstractCooking) DoDinner() {
11	if !d.foodEnough() {
12		d.doShopping()
13	}
14	d.beforeCooking()
15	fmt.Println(d.doCooking())
16	d.afterCooking()
17	d.eat()
18}
19
20func (d *AbstractCooking) eat() {
21	fmt.Println(fmt.Sprintf("%sMM说:开吃喽", d.Name))
22}

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

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

2.2.3 定义实现类

 1type HZDinner struct {
 2	AbstractCooking
 3}
 4
 5func NewHZDinner(name string) *HZDinner {
 6	c := new(HZDinner)
 7	c.Name = name
 8	// 选择实现的
 9	c.AbstractCooking.foodEnough = c.foodEnough
10	c.AbstractCooking.doShopping = doShopping
11	// 必须实现的
12	c.AbstractCooking.beforeCooking = c.beforeCooking
13	c.AbstractCooking.doCooking = c.doCooking
14	c.AbstractCooking.afterCooking = c.afterCooking
15	return c
16}
17
18func (c *HZDinner) foodEnough() bool {
19	return false
20}
21
22func (c *HZDinner) beforeCooking() {
23	println(fmt.Printf("%sMM 在洗菜切菜", c.Name))
24}
25
26func (c *HZDinner) doCooking() string {
27	return fmt.Sprintf("%sMM 在做%s菜", c.Name, c.Name)
28}
29
30func (c *HZDinner) afterCooking() {
31	println(fmt.Printf("%sMM 让你去品尝", c.Name))
32}
33
34type TWDinner struct {
35	AbstractCooking
36}
37
38func NewTWDinner(name string) *TWDinner {
39	c := new(TWDinner)
40	c.Name = name
41	// 选择实现的
42	c.AbstractCooking.foodEnough = c.foodEnough
43	c.AbstractCooking.doShopping = c.doShopping
44	// 必须实现的
45	c.AbstractCooking.beforeCooking = c.beforeCooking
46	c.AbstractCooking.doCooking = c.doCooking
47	c.AbstractCooking.afterCooking = c.afterCooking
48	return c
49}
50
51func (c *TWDinner) foodEnough() bool {
52	return false
53}
54
55func (c *TWDinner) doShopping() {
56	fmt.Println("生鲜超市购买,一定要买茶叶蛋")
57}
58
59func (c *TWDinner) beforeCooking() {
60	println(fmt.Printf("%sMM 在洗菜切菜", c.Name))
61}
62
63func (c *TWDinner) doCooking() string {
64	return fmt.Sprintf("%sMM 在做%s菜", c.Name, c.Name)
65}
66
67func (c *TWDinner) afterCooking() {
68	println(fmt.Printf("%sMM 让你去品尝", c.Name))
69}
70
71type BJDinner struct {
72	AbstractCooking
73}
74
75func NewBJDinner(name string) *BJDinner {
76	c := new(BJDinner)
77	c.Name = name
78	// 选择实现的
79	c.AbstractCooking.foodEnough = foodEnough
80	c.AbstractCooking.doShopping = doShopping
81	// 必须实现的
82	c.AbstractCooking.beforeCooking = c.beforeCooking
83	c.AbstractCooking.doCooking = c.doCooking
84	c.AbstractCooking.afterCooking = c.afterCooking
85	return c
86}
87
88func (c *BJDinner) beforeCooking() {
89	println(fmt.Printf("%sMM 在洗菜切菜", c.Name))
90}
91
92func (c *BJDinner) doCooking() string {
93	return fmt.Sprintf("%sMM 在做%s菜", c.Name, c.Name)
94}
95
96func (c *BJDinner) afterCooking() {
97	println(fmt.Printf("%sMM 让你去品尝", c.Name))
98}

2.2.4 定义默认实现方法

1func foodEnough() bool {
2	return true
3}
4
5func doShopping() {
6	fmt.Println("门口小贩买菜")
7}

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

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

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

2.2.5 运行例子

代码:

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

输出结果:

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

2.3 Golang版本2

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

2.3.1 定义接口

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

2.3.2 定义抽象类

 1type AbstractDinner struct {
 2}
 3
 4func (AbstractDinner) foodEnough() bool {
 5	return true
 6}
 7
 8func (AbstractDinner) doShopping() {
 9	fmt.Println("门口小贩买菜")
10}
11
12func (AbstractDinner) beforeCooking() {
13}
14
15func (AbstractDinner) doCooking() string {
16	return ""
17}
18
19func (AbstractDinner) afterCooking() {
20}

实现 Dinner2 接口,和下面等价

 1type AbstractDinner struct {
 2	Dinner2
 3}
 4
 5func (AbstractDinner) foodEnough() bool {
 6	return true
 7}
 8
 9func (AbstractDinner) doShopping() {
10	fmt.Println("门口小贩买菜")
11}

2.3.3 定义实现类

 1type HangzhouDinner struct {
 2	AbstractDinner
 3}
 4
 5func NewHangzhouDinner(name string) Dinner2 {
 6	return &HangzhouDinner{
 7		AbstractDinner{
 8			Name: name,
 9		},
10	}
11}
12
13func (d *HangzhouDinner) foodEnough() bool {
14	return false
15}
16
17func (d *HangzhouDinner) beforeCooking() {
18	fmt.Println(fmt.Sprintf("%sMM 在洗菜切菜", d.Name))
19}
20
21func (d *HangzhouDinner) doCooking() string {
22	return fmt.Sprintf("%sMM 在做%s菜", d.Name, d.Name)
23}
24
25func (d *HangzhouDinner) afterCooking() {
26	fmt.Println(fmt.Sprintf("%sMM 让你去品尝", d.Name))
27}
28
29type BeijingDinner struct {
30	AbstractDinner
31}
32
33func NewBeijingDinner(name string) Dinner2 {
34	return &BeijingDinner{
35		AbstractDinner{
36			Name: name,
37		},
38	}
39}
40
41func (d *BeijingDinner) beforeCooking() {
42	fmt.Println(fmt.Sprintf("%sMM 在洗菜切菜", d.Name))
43}
44
45func (d *BeijingDinner) doCooking() string {
46	return fmt.Sprintf("%sMM 在做%s菜", d.Name, d.Name)
47}
48
49func (d *BeijingDinner) afterCooking() {
50	fmt.Println(fmt.Sprintf("%sMM 让你去品尝", d.Name))
51}
52
53type TaiwanDinner struct {
54	AbstractDinner
55}
56
57func NewTaiwanDinner(name string) Dinner2 {
58	return &TaiwanDinner{
59		AbstractDinner{
60			Name: name,
61		},
62	}
63}
64
65func (d *TaiwanDinner) foodEnough() bool {
66	return false
67}
68
69func (d *TaiwanDinner) doShopping() {
70	fmt.Println("生鲜超市购买,一定要买茶叶蛋")
71}
72
73func (d *TaiwanDinner) beforeCooking() {
74	fmt.Println(fmt.Sprintf("%sMM 在洗菜切菜", d.Name))
75}
76
77func (d *TaiwanDinner) doCooking() string {
78	return fmt.Sprintf("%sMM 在做%s菜", d.Name, d.Name)
79}
80
81func (d *TaiwanDinner) afterCooking() {
82	fmt.Println(fmt.Sprintf("%sMM 让你去品尝", d.Name))
83}

2.3.4 定义模板方法

 1func DoDinner(d Dinner2) {
 2	if !d.foodEnough() {
 3		d.doShopping()
 4	}
 5
 6	d.beforeCooking()
 7	fmt.Println(d.doCooking())
 8	d.afterCooking()
 9  d.eat()
10}
11
12func (ad AbstractDinner) eat() {
13	fmt.Println(fmt.Sprintf("%sMM说:开吃喽", ad.Name))
14}

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

 1type Dinner2 interface {
 2	...
 3	DoDinner(d Dinner2)
 4}
 5
 6func (ad AbstractDinner) DoDinner(d Dinner2) {
 7	if !d.foodEnough() {
 8		d.doShopping()
 9	}
10
11	d.beforeCooking()
12	fmt.Println(d.doCooking())
13	d.afterCooking()
14	ad.eat()
15}
16
17func (ad AbstractDinner) eat() {
18	fmt.Println(fmt.Sprintf("%sMM说:开吃喽", ad.Name))
19}

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

2.3.5 运行例子

代码:

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

输出结果:

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

2.4、例子说明

三、开源框架使用场景

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

3.1 JDK AbstractList

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

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

 1
 2public class ArrayList<E> extends AbstractList<E>
 3        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
 4{
 5  	// ...
 6    public void add(int index, E element) {
 7        rangeCheckForAdd(index);
 8
 9        ensureCapacityInternal(size + 1);  // Increments modCount!!
10        System.arraycopy(elementData, index, elementData, index + 1,
11                         size - index);
12        elementData[index] = element;
13        size++;
14    }
15  	// ...
16}        
17

3.1 spring 中的 事务管理器

 1public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
 2 
 3  // ...
 4	public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
 5		Object transaction = doGetTransaction();
 6
 7		// Cache debug flag to avoid repeated checks.
 8		boolean debugEnabled = logger.isDebugEnabled();
 9
10		if (definition == null) {
11			// Use defaults if no transaction definition given.
12			definition = new DefaultTransactionDefinition();
13		}
14
15		if (isExistingTransaction(transaction)) {
16			// Existing transaction found -> check propagation behavior to find out how to behave.
17			return handleExistingTransaction(definition, transaction, debugEnabled);
18		}
19
20		// Check definition settings for new transaction.
21		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
22			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
23		}
24
25		// No existing transaction found -> check propagation behavior to find out how to proceed.
26		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
27			throw new IllegalTransactionStateException(
28					"No existing transaction found for transaction marked with propagation 'mandatory'");
29		}
30		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
31				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
32				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
33			SuspendedResourcesHolder suspendedResources = suspend(null);
34			if (debugEnabled) {
35				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
36			}
37			try {
38				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
39				DefaultTransactionStatus status = newTransactionStatus(
40						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
41				doBegin(transaction, definition);
42				prepareSynchronization(status, definition);
43				return status;
44			}
45			catch (RuntimeException ex) {
46				resume(null, suspendedResources);
47				throw ex;
48			}
49			catch (Error err) {
50				resume(null, suspendedResources);
51				throw err;
52			}
53		}
54		else {
55			// Create "empty" transaction: no actual transaction, but potentially synchronization.
56			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
57				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
58						"isolation level will effectively be ignored: " + definition);
59			}
60			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
61			return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
62		}
63	}
64  // ...
65    
66	protected abstract void doBegin(Object transaction, TransactionDefinition definition)
67			throws TransactionException;    
68  
69  ...
70}

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

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

 1public class GongDaoDataSourceTransactionManager extends DataSourceTransactionManager implements
 2    ResourceTransactionManager, InitializingBean, EnvironmentAware {
 3  
 4  	// ...
 5    protected void doBegin(Object transaction, TransactionDefinition definition) {
 6        String currentXid;
 7        if (XIDContext.getCurrent() != null && null != XIDContext.getCurrent().getId()) {
 8            currentXid = xidGenerator.getXID();
 9            XIDContext.childCurrent(new TransactionContent(currentXid, definition.getName()));
10        } else {
11            currentXid = xidGenerator.getXID();
12            XIDContext.setXid(new TransactionContent(currentXid, definition.getName()));
13        }
14
15        if (null == HyjalTransactionFileAppender.contextHolder.get()) {
16            String logFileName = HyjalTransactionFileAppender.makeLogFileName(new Date(),
17                HyjalTransactionFileAppender.logId.get());
18            HyjalTransactionFileAppender.contextHolder.set(logFileName);
19        }
20
21        try {
22            if (null == XIDContext.getCurrentXid()) {
23                XIDContext.setXid(xidGenerator.getXID());
24            }
25            super.doBegin(transaction, definition);
26        } finally {
27            if (logger.isDebugEnabled()) {
28                logger.debug("do begin Xid : {}", currentXid);
29                HyjalTransactionLogger.log("do begin Xid : {}, obj : {}", currentXid, definition.getName());
30            }
31        }
32    }
33  	// ...
34}  

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

四、优势和劣势

4.1 优势

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

4.2 劣势

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

五、参考

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