模板模式

案例引入

制作豆浆问题

要求:
1.制作豆浆流程 选材->添加配料->浸泡->放到豆浆机打碎。
2.通过添加不同配料,可以制作出不同口味的豆浆。
3.选材,浸泡和放到豆浆机打碎这些步骤对于制作每种口味的豆浆都是一样的。
4.请使用模板模式,完成。(因为模板方法模式,简单,就直接写了)

基本介绍

  • 1.模板模式(Template Pattern),又叫模板方法模式(Template Method Pattern),在一个抽象类中,定义执行方法的模板,其子类可以按需重写方法实现,但使用将以抽象类中定义的方式执行。
  • 2.模板模式,就是在抽象类中,定义了一个操作中算法的骨架,将具体的一些步骤,延迟到了子类中实现,使得子类可以不改变一个算法的结构,就可以重新定义算法的某些特定步骤。
  • 3.这种类型的设计模式属于行为模式。

原理类图

模板模式-小白菜博客
对类图的说明
1.AbstractClass抽象类,类中实现了模板方法,定义了算法的骨架,其他的抽象方法由具体子类去实现,如operation2,3,4方法
2.ConcreteClass 实现抽象方法operation2,3,4,以完成算法中子类特定的步骤。

模板模式实现制作豆浆案例

案例uml类图

代码实现
/**
 * @author 长名06
 * @version 1.0
 * 豆浆父类,模板类
 */
public abstract class SoyaMilk {
    public final void make(){
        select();
        add();
        soak();
        beat();
    }

    /**
     * 选材
     */
    public void select(){
        System.out.println("第一步,选择黄豆作为材料");
    }

    /**
     * 添加配料
     */
    public abstract void add();

    /**
     * 浸泡
     */
    public void soak(){
        System.out.println("第三步,黄豆和配料开始浸泡,需要3小时");
    }

    /**
     * 打碎
     */
    public void beat(){
        System.out.println("第四步,黄豆和配料放到豆浆机去打碎");
    }
}
/**
 * @author 长名06
 * @version 1.0
 */
public class RedBeanSoyaMilk extends SoyaMilk{
    @Override
    public void add() {
        System.out.println("第二步,加入上好的红豆");
    }
}
/**
 * @author 长名06
 * @version 1.0
 */
public class PeanutSoyaMilk extends SoyaMilk {
    @Override
    public void add() {
        System.out.println("第二步,加入上好的花生");
    }
}
/**
 * @author 长名06
 * @version 1.0
 */
public class Client {
    public static void main(String[] args) {
        //制作红豆豆浆
        SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
        redBeanSoyaMilk.make();

        System.out.println("----------");

        //制作花生豆浆
        SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
        peanutSoyaMilk.make();
    }
}

模板模式的钩子方法

  • 1.在模板模式的抽象类中,可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为"钩子"。
  • 2.用上面案例做演示,希望制作纯豆浆,不叫任何配料。使用钩子方法,对前面实现做改进。
/**
 * @author 长名06
 * @version 1.0
 * 豆浆父类,模板类
 */
public abstract class SoyaMilk {
    public final void make(){
        select();
        if(isAdd()){
            add();
        }
        soak();
        beat();
    }

    /**
     * 选材
     */
    public void select(){
        System.out.println("第一步,选择黄豆作为材料");
    }

    /**
     * 添加配料
     */
    public abstract void add();

    /**
     * 浸泡
     */
    public void soak(){
        System.out.println("第三步,黄豆和配料开始浸泡,需要3小时");
    }

    /**
     * 打碎
     */
    public void beat(){
        System.out.println("第四步,黄豆和配料放到豆浆机去打碎");
    }

    /**
     * 是否需要添加原料,这样设计,可以设置为,子类空实现add步骤,但是不进行添加
     * 勾子方法,由这个方法的返回值,判断是否执行那个需要被子类重写的方法,钩子方法
     * 是可以被子类继承,由具体子类决定是否执行
     * @return
     */
    public boolean isAdd(){
        return true;
    }
}
/**
 * @author 长名06
 * @version 1.0
 */
public class RedBeanSoyaMilk extends SoyaMilk {
    @Override
    public void add() {
        System.out.println("第二步,加入上好的红豆");
    }
}
/**
 * @author 长名06
 * @version 1.0
 * 纯豆浆
 */
public class PureBeanSoyaMilk extends SoyaMilk{
    @Override
    public void add() {
        System.out.println("第二步,制作纯豆浆,不加任何配料");
    }

    @Override
    public boolean isAdd() {
        return !super.isAdd();//不执行第二步
    }
}
/**
 * @author 长名06
 * @version 1.0
 * 花生豆浆
 */
public class PeanutSoyaMilk extends SoyaMilk {
    @Override
    public void add() {
        System.out.println("第二步,加入上好的花生");
    }
}
/**
 * @author 长名06
 * @version 1.0
 */
public class Client {
    public static void main(String[] args) {
        //制作红豆豆浆
        SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
        redBeanSoyaMilk.make();

        System.out.println("----------");

        //制作花生豆浆
        SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
        peanutSoyaMilk.make();

        System.out.println("----------");

        //制作纯豆浆
        SoyaMilk pureBeanSoyaMilk = new PureBeanSoyaMilk();
        pureBeanSoyaMilk.make();
    }
}

注意事项和细节

  • 1.基本思想是,算法只存在于一个地方,也就是父类中,容易修改。需要修改算法时,只要修改父类的模板方法,或者已经实现了某些步骤,子类就会继承这些修改。
  • 2.实现了最大化代码复用,父类的模板方法,和已实现的某些步骤会被子类继承而直接使用。
  • 3.即统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
  • 4.该模式的不足之处,每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。
  • 5.一般模板方法都加上final关键字,防止子类重写模板方法。
  • 6.模板方法,使用场景,当要完成某个过程,该过程要执行一系列步骤,这一系列步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式做处理。

模板模式在Spring框架中的源码分析

    //该方法是AbstractApplicationContext类中的方法,类似模板模式中的模板方法(定义骨架的方法),案例中的make()
    @Override
	public void refresh() throws BeansException, IllegalStateException {
        //定义容器初始化的模板,空方法由子类实现,由子类调用完成,容器的初始化,AbstractApplicationContext的子类有AbstractRefreshableApplicationContext和              
        // GenericApplicationContext
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
    //该方法是AbstractApplicationContext类中的方法
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();//这个方法,在本类中是抽象方法,由子类去实现
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}
    //该方法是AbstractApplicationContext类中的方法 类似模板模式中的抽象方法,需要子类实现,案例中的add()方法
    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。