设计模式之模板方法模式(Template Method)

 模板方法模式的运用非常广泛,JDK的类加载器、Spring的IOC都有使用模板方法模式。

模板方法模式:一般是为了统一子类的算法实现步骤,父类定义了框架(调用哪些方法及顺序),某些特定方法由子类实现。

父类模板方法中有两类方法:

1、共同的方法:所有子类都会用到的代码

2、不同的方法:子类要覆盖的方法,分为两种:

  A、抽象方法:父类中的是抽象方法,子类必须覆盖

  B、钩子方法:父类中是一个空方法,子类继承了默认也是空的

钩子反法主要为了不强制子类实现不必要的抽象方法,但又不剥夺子类自由选择的权利,我们在父类提供一个默认的空实现,来让子类自由选择是否要覆盖掉这些方法。

 

Java代码展示

定义接口

public interface PageBuilder {
    String bulidHtml();
}

定义抽象类

public abstract class AbstractPageBuilder implements PageBuilder {

    private StringBuffer stringBuffer = new StringBuffer();

    @Override
    public String bulidHtml() {
        //首先加入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();
    }

    //第一个模板方法
    protected abstract void appendHead(StringBuffer stringBuffer);

    //第二个模板方法
    protected abstract void appendBody(StringBuffer stringBuffer);

    //钩子方法1
    protected void appendMeta(StringBuffer stringBuffer){
    }
    //钩子方法2
    protected void appendLink(StringBuffer stringBuffer){
    }
}

子类继承

public class MyPageBuilder extends AbstractPageBuilder{
    @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 class Test {

    public static void main(String[] args) {
        PageBuilder pageBuilder = new MyPageBuilder();
        System.out.println(pageBuilder.bulidHtml());
    }
}

结果

<!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>

 

 

类加载器源码

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);
    }

}

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

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

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

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

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

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

 

 

 

GitHub地址:https://github.com/xxiangzh/design-pattern

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值