模版方法(template method)
在模板模式中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
关键代码:在抽象类实现,其他步骤在子类实现。
使用场景: 通常情况下,模板方法模式用于定义构建某个对象的步骤与顺序,或者定义一个算法的骨架。
注意事项:
对于模板方法模式,父类提供的构建步骤和顺序或者算法骨架,通常是不希望甚至是不允许子类去覆盖的,所以在某些场景中,可以直接将父类中提供骨架的方法声明为final类型。
典型应用:ClassLoader.findClass(String name)
类图:
public abstract class ClassLoader { //定义一个父加载器的引用 private final ClassLoader parent; //父类算法的定义 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name);//1.先从缓存中查找当前类是否已经加载,如果找到直接返回 if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false);//2.递归查找父加载器中是否已经加载此类 } else { c = findBootstrapClassOrNull(name);//3.顶级父加载器BootstrapClassLoader是否已经此类 } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. //依然未找到,自己加载当前类 long t1 = System.nanoTime(); c = findClass(name);//典型模版方法 // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } //抽象父类中的findClass方法并没有实现,只抛出了一个异常,交给子类选择性重写 protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } }
类加载器为何要使用模板模式给我们定义好查找的算法,是为了保证我们加载的每一个类在虚拟机当中都有且仅有一个。
既然如此,为何不把loadClass方法写成final类型的,这样不是更安全吗?
这是因为有的时候我们希望JVM当中每一个类有且仅有一个,但有的时候我们希望有两个,甚至N个,就比如我们的tomcat,你可以想象下,你每一个项目假设都有com.xxx.xxxx.BaseDao等等,如果这些类都是一个的话,你的tomcat还能同时启动多个WEB服务吗?虽说tomcat也是遵循的双亲委派模型,但是从此也可以看出来,我们并不是在所有时候都希望同一个全限定名的类在整个JVM里面只有一个。
参考资料:https://blog.csdn.net/zuoxiaolong8810/article/details/9108189