模板方法模式

模板方法模式,一般是为了统一子类的算法实现步骤,所使用的一种手段或方式。它在父类定义一系列的算法步骤,而将具体实现推迟到子类。

通常情况下,模板方法模式用于定义构建某个对象的步骤与顺序,或定义一个算法的骨架。

通常来说父类定义的步骤和顺序或算法骨架,是不允许子类覆盖的,所以在某些场景中,可以直接将父类提供的骨架方法声明为final类型的。

下面模拟构建一个html页面,来应用模板方法模式:

父类定义构建一个html页面的步骤算法:

 

public abstract class AbstractPageBuild {

	private StringBuffer stringBuffer = new StringBuffer();

	/**
	 * 按顺序构建算法骨架
	 * 
	 * @return
	 */
	public String build() {
		// 首先加入doctype,因为都是html页面,父类不需要推迟给子类实现,直接在父类实现
		stringBuffer
				.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
		// 页面下面就是成对的Html标签,也不需要子类实现,直接在父类实现
		stringBuffer.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
		// 下面就是Head标签里的内容,我们父类做不了主,推迟到子类实现,所以我们定义一个抽象方法,让子类必须实现
		appendHead(stringBuffer);
		// 下面是Body标签的内容,我们父类依然做不了主,推迟到子类实现
		appendBody(stringBuffer);
		// html的关闭标签
		stringBuffer.append("</html>");
		return stringBuffer.toString();
	}

	/**
	 * 模板方法-添加Head
	 * 
	 * @param stringBuffer
	 */
	protected abstract void appendHead(StringBuffer stringBuffer);

	/**
	 * 模板方法-添加body
	 * 
	 * @param stringBuffer
	 */
	protected abstract void appendBody(StringBuffer stringBuffer);
}
public class MyPageBuild extends AbstractPageBuild {

	@Override
	protected void appendHead(StringBuffer stringBuffer) {
		stringBuffer.append("<head><title>你好</title></head>");
	}

	@Override
	protected void appendBody(StringBuffer stringBuffer) {
		stringBuffer.append("<body><h1>你好,世界!</h1></body>");
	}

	public static void main(String[] args) {
		AbstractPageBuild page = new MyPageBuild();
		String html = page.build();
		System.out.println(html);
	}

}

子类继承父类,并实现父类必须要实现的方法,来完成一个html页面构建。

并通过多态来调用父类的构建方法。

执行结果如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>你好</title></head><body><h1>你好,世界!</h1></body></html>

以上就是模板方法模式最基本实现。

 

模板方法还有一种使用方式,为了给子类足够的自由度,可以提供一些方法供子类覆盖,去实现一些骨架中不是必须但却可以自定义实现的步骤。

比如将head标签细化为title,meta,link,script,除了title,body子类必须覆盖外,其它可选择性覆盖

public abstract class AbstractPageBuild2 {

	private StringBuffer stringBuffer = new StringBuffer();

	/**
	 * 按顺序构建算法骨架
	 * 
	 * @return
	 */
	public String build() {
		// 首先加入doctype,因为都是html页面,父类不需要推迟给子类实现,直接在父类实现
		stringBuffer
				.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
		// 页面下面就是成对的Html标签,也不需要子类实现,直接在父类实现
		stringBuffer.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
		// 下面就是Head标签里的内容,我们父类做不了主,推迟到子类实现,所以我们定义一个抽象方法,让子类必须实现
		stringBuffer.append("<head>");
		appendTitle(stringBuffer);
		appendMeta(stringBuffer);
		appendLink(stringBuffer);
		appendScript(stringBuffer);
		stringBuffer.append("</head>");
		// 下面是Body标签的内容,我们父类依然做不了主,推迟到子类实现
		appendBody(stringBuffer);
		// html的关闭标签
		stringBuffer.append("</html>");
		return stringBuffer.toString();
	}

	/**
	 * 子类选择性覆盖
	 * 
	 * @param stringBuffer
	 */
	protected void appendMeta(StringBuffer stringBuffer) {
	}

	/**
	 * 子类选择性覆盖
	 * 
	 * @param stringBuffer
	 */
	protected void appendLink(StringBuffer stringBuffer) {
	}

	/**
	 * 子类选择性覆盖
	 * 
	 * @param stringBuffer
	 */
	protected void appendScript(StringBuffer stringBuffer) {
	}

	/**
	 * 模板方法-添加Title-子类必须覆盖
	 * 
	 * @param stringBuffer
	 */
	protected abstract void appendTitle(StringBuffer stringBuffer);

	/**
	 * 模板方法-添加body-子类必须覆盖
	 * 
	 * @param stringBuffer
	 */
	protected abstract void appendBody(StringBuffer stringBuffer);
}

如果想让子类选择性覆盖,可以将方法实现为一个空方法。

public class MyPageBuild2 extends AbstractPageBuild2 {

	@Override
	protected void appendMeta(StringBuffer stringBuffer) {
		stringBuffer.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />");
	}

	@Override
	protected void appendTitle(StringBuffer stringBuffer) {
		stringBuffer.append("<title>你好</title>");
	}

	@Override
	protected void appendBody(StringBuffer stringBuffer) {
		stringBuffer.append("<body><h1>你好,世界!</h1></body>");
	}

	public static void main(String[] args) {
		AbstractPageBuild2 page = new MyPageBuild2();
		String html = page.build();
		System.out.println(html);
	}

}

子类可以自由选择性覆盖父类的方法,通过多态来调用父类的构建算法过程

执行结果如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>你好</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body><h1>你好,世界!</h1></body></html>

通常情况下,模板方法模式用于定义构建某个对象的步骤与顺序,或者定义一个算法的骨架。

               我们刚才的示例明显就是构建一个String对象的过程,在这里要声明一点,对于模板方法模式,父类提供的构建步骤和顺序或者算法骨架,通常是不希望甚至是不允许子类去覆盖的,所以在某些场景中,可以直接将父类中提供骨架的方法声明为final类型。

 

说到模板方法模式,我们JDK当中有一个类与它还有一个不得不说的故事,那就是类加载器。

                JDK类加载器可以大致分为三类,分别是启动类加载器,扩展类加载器,以及应用程序加载器。

                这三者加载类的路径分别为如下:

                启动类加载器:JAVA_HOME/lib目录下,以及被-Xbootcalsspath参数设定的路径,不过启动类加载器加载的类是有限制的,如果JVM不认识的话,你放在这些目录下也没用。

                扩展类加载器:JAVA_HOME/lib/ext目录下,以及被java.ext.dirs系统变量指定的路径。

                应用程序类加载器:用户自己的类路径(classpath),这个类加载器就是我们经常使用的系统类加载器,并且JDK中的抽象类ClassLoader的默认父类加载器就是它。

                在这里为什么说类加载器和模板方法模式有关呢,是因为ClassLoader类就使用了模板模式,去保证类加载过程中的唯一性。LZ先给各位看下这个类当中的模板模式的应用。

public abstract class ClassLoader {
    //这是一个重载方法
    public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
    }
    
    //这里就是父类算法的定义
    protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
        if (parent != null) {
            c = parent.loadClass(name, false);
        } else {
            c = findBootstrapClass0(name);
        }
        } catch (ClassNotFoundException e) {
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
    }
    //这里留了一个方法给子类选择性覆盖
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
    }

}

LZ截取了主要的部分,为了突出这三个方法。在上面LZ加了简单的注释,相信经过刚才的介绍,各位应该能看出来这是一个模板方法模式,只是它没有定义抽象方法,因为findClass这个方法,并不是必须实现的,所以JDK选择留给程序员们自己选择是否要覆盖。

              从代码上我们可以看出,在ClassLoader中定义的算法顺序是。

              1,首先看是否有已经加载好的类。

              2,如果父类加载器不为空,则首先从父类类加载器加载。

              3,如果父类加载器为空,则尝试从启动加载器加载。

              4,如果两者都失败,才尝试从findClass方法加载。

              这是JDK类加载器的双亲委派模型,即先从父类加载器加载,直到继承体系的顶层,否则才会采用当前的类加载器加载。这样做的目的刚才已经说了,是为了JVM中类的一致性。

 

转自:http://www.cnblogs.com/zuoxiaolong/p/pattern10.html 

      

 

<audio controls="controls" style="display: none;"></audio>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值