zoukankan      html  css  js  c++  java
  • 理解模板模式

    转载:

    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等

     待更新...

  • 相关阅读:
    马氏距离的深入理解
    Mahalanobis Distance(马氏距离)
    Weka EM 协方差
    数据挖掘、概率分析与决策支持
    二、 Android中gravity与layout_gravity的区别
    一、 Android完全退出应用程序
    python configparse
    时间戳与时间互转
    python argparse
    时间插件
  • 原文地址:https://www.cnblogs.com/shaohsiung/p/9529251.html
Copyright © 2011-2022 走看看