转载:
http://www.zuoxiaolong.com/blog/article.ftl?id=116
http://www.iteye.com/topic/713770?1306420721
一. 什么是模板模式
一般情况下, 为了统一子类的算法实现步骤,所使用的一种手段.
二. 如何使用模板模式
下面通过使用模板模式生成一个HTML页面代码的例子, 让大家理解模板模式的核心要点:
1. 将骨架方法抽取成接口, 有利于维护和扩展.
1 interface HtmlPageBuilder { 2 // 骨架方法 3 String bulidHtml(); 4 }
2. 由一个抽象类实现该接口, 并定义骨架方法的核心逻辑.
1 abstract class AbstractHtmlPageBuilder implements HtmlPageBuilder { 2 3 private StringBuffer buffer = new StringBuffer(); 4 5 // 通常是不希望甚至是不允许子类去覆盖的,所以在某些场景中,可以直接将骨架方法声明为final类型 6 @Override 7 public String bulidHtml() { 8 // html开始标签 9 buffer.append("<html>"); 10 // head, 引用appendHead模板方法 11 appendHead(buffer); 12 // body, 引用appendBody模板方法 13 appendBody(buffer); 14 // script 15 appendScript(buffer); 16 // html关闭标签 17 buffer.append("</html>"); 18 return buffer.toString(); 19 } 20 21 // 模板方法 22 // 使用abstract修饰的模板方法表明这些方法都会被bulidHtml方法调用而且是必须的 23 protected abstract void appendHead(StringBuffer buffer); 24 25 // 模板方法 26 protected abstract void appendBody(StringBuffer buffer); 27 28 // 模板方法, 允许子类选择性实现, 默认空实现 29 protected void appendScript(StringBuffer buffer) {} 30 }
这里要注意的是:
1) 模板方法通常在骨架方法中被调用.
2) 抽象模板方法要求子类必须实现.
3) 非抽象模板方法允许子类选择性实现, 默认空实现.
4) 骨架方法通常是不允许子类去覆盖的,所以在某些场景中,可以直接将骨架方法声明为final类型.
3. 实现类
继承抽象类, 实现抽象类中所有抽象模板方法, 非抽象模板方法可以选择性实现
1 class MyHtmlPageBuilder extends AbstractHtmlPageBuilder { 2 3 @Override 4 protected void appendHead(StringBuffer buffer) { 5 buffer.append("<head><title>My HTML Page</title></head>"); 6 } 7 8 @Override 9 protected void appendBody(StringBuffer buffer) { 10 buffer.append("<body><h1>Welcome To My Website.</h1></body>"); 11 } 12 13 // 启动类 14 public static void main(String[] args) { 15 System.out.println(new MyHtmlPageBuilder().bulidHtml()); 16 } 17 }
三. 模板模式要点小结:
1 将骨架方法提取成一个接口
2 该接口由一个抽象类实现, 并定义核心流程
3 抽象类中存在若干模板方法, 具体有两类: 抽象方法和一般方法空实现
1) 抽象方法要求子类必须实现
2) 一般方法空实现允许子类选择性实现
四. 模板模式的实际运用
1. 类加载器的父类委托机制
三类JDK类加载器,分别是启动类加载器,扩展类加载器,以及应用程序加载器。
三类加载类的路径分别为:
启动类加载器:JAVA_HOME/lib目录下,以及被-Xbootcalsspath参数设定的路径,不过启动类加载器加载的类是有限制的,如果JVM不认识的话,你放在这些目录下也没用。
扩展类加载器:JAVA_HOME/lib/ext目录下,以及被java.ext.dirs系统变量指定的路径。
应用程序类加载器:用户自己的类路径(classpath),这个类加载器就是我们经常使用的系统类加载器,并且JDK中的抽象类ClassLoader的默认父类加载器就是它。
ClassLoader类就使用了模板模式,目的是为了保证类加载过程中的唯一性.
1 public abstract class ClassLoader { 2 // 重载方法 3 public Class<?> loadClass(String name) throws ClassNotFoundException { 4 return loadClass(name, false); 5 } 6 7 // 骨架 8 protected synchronized Class<?> loadClass(String name, boolean resolve) 9 throws ClassNotFoundException 10 { 11 Class c = findLoadedClass(name); 12 if (c == null) { 13 try { 14 if (parent != null) { 15 c = parent.loadClass(name, false); 16 } else { 17 c = findBootstrapClass0(name); 18 } 19 } catch (ClassNotFoundException e) { 20 c = findClass(name); 21 } 22 } 23 if (resolve) { 24 resolveClass(c); 25 } 26 return c; 27 } 28 29 // 模板方法, 允许子类选择性覆盖 30 protected Class<?> findClass(String name) throws ClassNotFoundException { 31 throw new ClassNotFoundException(name); 32 } 33 }
ClassLoader中loadClass方法中定义的算法顺序为:
1) 查看是否存在已加载好的Class对象, 如果存在则返回该对象, 否则继续向下执行.
2) 如果父类加载器不为空,则首先从父类类加载器加载.
3) 如果父类加载器为空,则尝试从启动加载器加载.
4) 如果两者都失败,才尝试从findClass方法加载.
这是JDK类加载器的父类委拖机制,即先从父类加载器加载,直到继承体系的顶层,否则才会采用当前的类加载器加载. 这样做的目的是为了保证JVM中类的一致性.
如果没有按照ClassLoader中提供的骨架算法去加载类的话,可能会造成JVM中有两个一模一样的类信息,他们是来自一个类文件,但却不是一个加载器加载的,所以这两个类不相等.
1 class MyClassLoader extends ClassLoader{ 2 3 public Class<?> loadClass(String name) throws ClassNotFoundException { 4 String fileName = name.substring(name.lastIndexOf(".")+1) + ".class"; 5 InputStream is = getClass().getResourceAsStream(fileName); 6 if (is == null) { 7 return super.loadClass(name); 8 } 9 try { 10 byte[] b = new byte[is.available()]; 11 is.read(b); 12 return defineClass(name, b, 0, b.length); 13 } catch (IOException e) { 14 throw new ClassNotFoundException(); 15 } 16 } 17 18 } 19 20 public class ClassLoaderTest { 21 22 public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { 23 ClassLoader classLoader = new MyClassLoader(); 24 Class<?> clazz = classLoader.loadClass("com.classloader.ClassLoaderTest"); 25 Object entity = clazz.newInstance(); 26 System.out.println(entity instanceof ClassLoaderTest); 27 } 28 }
这就是类加载器为何要使用模板模式给我们定义好查找的算法,是为了保证加载的每一个类在JVM当中都有且仅有一个.
为何不把loadClass方法写成final类型的,不是更安全吗?
这是因为有的时候我们希望JVM当中每一个类有且仅有一个,但有的时候我们希望有两个,甚至N个.
比如我们的tomcat,你可以想象下,你每一个Web工程假设都有com.xxx.UserDao等,如果这些类都是一个的话,你的tomcat还能同时启动多个WEB服务吗?虽说tomcat也是遵循的父类委托机制,但是从此也可以看出来,我们并不是在所有时候都希望同一个全限定名的类在整个JVM里面只有一个。
补充说明:
1) 一个tomcat -> 一个JVM -> 多个Web服务(网站)
2) 一个类通常情况下, 会被同一个类加载器加载, 所以这两个类是相等的, 也就是说如果一个类被不同类加载器加载, 那么这两个类不相等.
2. spring中的JdbcTemplate和HibernateTemplate等
待更新...