理解模板模式

转载:

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等

 待更新...

posted on 2018-08-24 13:11  ert999  阅读(376)  评论(0编辑  收藏  举报

导航