模板模式

模板模式

首先來看一下模板模式的簡介:模板模式(Template Pattern),一是指定義一個算法的骨架,并允許子類為一個或者多個步驟提供實現(xiàn)。模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法的某些步驟,屬于行為性設(shè)計模式。模板方法適用于以下應(yīng)用場景:

1、一次性實現(xiàn)一個算法的不變的部分,并將可變的行為留給子類來實現(xiàn)。

2、各子類中公共的行為被提取出來并集中到一個公共的父類中,從而避免代碼重復(fù)。

生活中的很多小事都是模板模式的體現(xiàn),比如我們?nèi)メt(yī)院看病的流程:1.掛號 2.看病

3.做檢查 4.拿藥,下面我們通過代碼來體會一下。

首先寫一個看病的抽象類SeeDoctor,特別提一下,在這個類中有一個鉤子方法needChecked,關(guān)于鉤子方法的定義:通過一個方法來干涉另一個方法的行為,就是一個方法的返回結(jié)果或修改的變量是另一個方法執(zhí)行時的if判斷條件或for/while循環(huán)調(diào)用條件的。在模板模式中,鉤子方法和模板模式?jīng)]有必然的關(guān)系,也就是說模板模式中可以有鉤子方法也可以沒有鉤子方法,具體是否使用鉤子方法根據(jù)業(yè)務(wù)判斷。

/**
 * @author: Winston
 * @createTime: 2021/6/28
 * <p>
 * 模板會有一個或者多個未實現(xiàn)的方法
 * 而且這些未實現(xiàn)的方法有固定的執(zhí)行順序
 * 看病流程
 */
public abstract class SeeDoctor {

    /**
     * 去醫(yī)院看病的流程
     */
    protected void goToHospital() {
        // 1.掛號
        this.register();
        // 2.找醫(yī)生看病
        this.lookingForDoctor();
        // 3.做檢查,有些病人是需要做檢查的,而有些病人是不需要做檢查的
        // 這里的needChecked()方法就是鉤子方法
        if (needChecked()) {
            // 檢查方法,病人做檢查的項目不一樣
            this.checked();
        }
        // 4.拿藥
        this.takeMedicine();
    }

    public final void takeMedicine() {
        System.out.println("根據(jù)藥方拿藥");
    }

    protected abstract void checked();


    // 鉤子方法,鉤子方法的定義:通過一個方法來干涉另一個方法的行為,就是一個方法的返回結(jié)果或修改的變量是另一個方法執(zhí)行時的if判斷條件或for/while循環(huán)調(diào)用條件的
    protected boolean needChecked(){
        return false;
    }

    public final void lookingForDoctor() {
        System.out.println("找醫(yī)生診斷");
    }

    public final void register() {
        System.out.println("掛號");
    }

}

編寫張三看病類ZhangSanSeeDoctor

public class ZhangSanSeeDoctor extends SeeDoctor {

    @Override
    protected void checked() {
        System.out.println("張三做的檢查項目:");
    }
}

編寫李四看病類LiSiSeeDoctor

public class LiSiSeeDoctor extends SeeDoctor {

    /**
     * 是否需要檢查
     */
    private boolean needChecked = false;

    /**
     * 通過構(gòu)造方法將needChecked的值改變
     * @param needChecked
     */
    public LiSiSeeDoctor(boolean needChecked) {
        this.needChecked = needChecked;
    }

    @Override
    protected boolean needChecked() {
        return this.needChecked;
    }

    @Override
    protected void checked() {
        System.out.println("李四做的檢查項目:......");
    }
}

測試類

public class SeeDoctorTest {
    public static void main(String[] args) {
        System.out.println("================張三去醫(yī)院看病========================");
        SeeDoctor patient1 = new ZhangSanSeeDoctor();
        patient1.goToHospital();

        System.out.println("================李四去醫(yī)院看病========================");
        SeeDoctor patient2 = new LiSiSeeDoctor(true);
        patient2.goToHospital();
    }
}

運行結(jié)果:

================張三去醫(yī)院看病========================
掛號
找醫(yī)生診斷
根據(jù)藥方拿藥
================李四去醫(yī)院看病========================
掛號
找醫(yī)生診斷
李四做的檢查項目:......
根據(jù)藥方拿藥

利用模板模式重構(gòu)JDBC操作業(yè)務(wù)場景

創(chuàng)建一個末班類JdbcTemplate,封裝所有的JDBC操作。以查詢?yōu)槔?,每次查詢的表不同,返回的?shù)據(jù)結(jié)構(gòu)也就不一樣。我們針對不同的數(shù)據(jù),都要封裝成不同的實體對象。而每個實體封裝的邏輯都是不一樣的,但封裝前和封裝后的處理流程是不變的,因此,我們可以使用模板方法模式來設(shè)計這樣的業(yè)務(wù)場景。

先創(chuàng)建約束ORM邏輯的接口RowMapper

/**
 * @author: Winston
 * @createTime: 2021/6/28
 *
 * Spring中RowMapper接口的作用,用來把數(shù)據(jù)庫中的列字段和java bean中屬性對應(yīng)上
 */
public interface RowMapper<T> {

    public T mapRow(ResultSet resultSet, int rowNumber);

}

創(chuàng)建封裝了所有處理流程的抽象類JdbcTemplate

/**
 * @author: Winston
 * @createTime: 2021/6/28
 */
public abstract class JdbcTemplate {

    /**
     * 數(shù)據(jù)源
     */
    private DataSource dataSource;

    public JdbcTemplate(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public List<?> executeQuery(String sql, RowMapper rowMapper, Object... args) throws Exception {
        // 1.獲取連接
        Connection connection = this.getConnection();
        // 2.創(chuàng)建語句集
        PreparedStatement preparedStatement = this.createPrepareStatement(connection, sql);
        // 3.執(zhí)行語句集
        ResultSet resultSet = this.executeQuery(preparedStatement, args);
        // 4.處理結(jié)果集
        List<?> result = this.parseResultSet(resultSet, rowMapper);
        // 5.關(guān)閉結(jié)果集
        this.closeResult(resultSet);
        // 6.關(guān)閉語句集
        this.closePreparedStatement(preparedStatement);
        // 7.關(guān)閉連接
        this.closeConnection(connection);

        return result;
    }

    protected void closeConnection(Connection connection) throws SQLException {
        connection.close();
    }

    protected void closePreparedStatement(PreparedStatement preparedStatement) throws SQLException {
        preparedStatement.close();
    }

    protected void closeResult(ResultSet resultSet) throws SQLException {
        resultSet.close();
    }

    protected List<?> parseResultSet(ResultSet resultSet, RowMapper rowMapper) throws SQLException {
        List<Object> result = new ArrayList<>();
        int rowNum = 1;
        while (resultSet.next()) {
            result.add(rowMapper.mapRow(resultSet, rowNum++));
        }
        return result;
    }

    protected ResultSet executeQuery(PreparedStatement preparedStatement, Object[] args) throws SQLException {
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i, args[i]);
        }

        return preparedStatement.executeQuery();
    }


    protected PreparedStatement createPrepareStatement(Connection connection, String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

    protected Connection getConnection() throws SQLException {
        return this.dataSource.getConnection();
    }
}

創(chuàng)建實體類User

public class User {
    private String username;
    private String password;
    private String mobile;
    private Integer age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

創(chuàng)建數(shù)據(jù)庫操作類UserDao

/**
 * @author: Winston
 * @createTime: 2021/6/28
 */
public class UserDao extends JdbcTemplate{

    public UserDao(DataSource dataSource) {
        super(dataSource);
    }


    public List<?> selectAll() throws Exception {
        String sql = "SELECT * FROM t_user";

        return super.executeQuery(sql, new RowMapper() {
            @Override
            public Object mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
                // 將實體類和數(shù)據(jù)庫字段對應(yīng)上
                String username = resultSet.getString("username");
                String password = resultSet.getString("password");
                String mobile = resultSet.getString("mobile");
                Integer age = resultSet.getInt("age");

                User user = new User();
                user.setUsername(username);
                user.setPassword(password);
                user.setMobile(mobile);
                user.setAge(age);

                return user;
            }
        }, null);
    }
}


測試類,這里由于沒有配置Spring環(huán)境和數(shù)據(jù)庫環(huán)境,僅做模擬。

public class UserTest {
    public static void main(String[] args) {
        DataSource dataSource = null;
        UserDao userDao = new UserDao(dataSource);
        try {
            List<?> result = userDao.selectAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

模板模式在JDK源碼中的體現(xiàn)

先來看看JDK中AbstractList的get()方法,可以看到get()是一個抽象方法,我們經(jīng)常用的ArrayList就是AbstractList的子類,由于AbstractList不同的子類所實現(xiàn)的get()方法有所不同,所以這也是模板模式的一種體現(xiàn)。

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    ... 
    abstract public E get(int index);
    ...
}

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    ...
     public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    ...
}

模板模式小結(jié)

模板模式就好比是造房子,大致的步驟是規(guī)定好的,比如先打地基、然后買水泥黃沙等,至于房子蓋的形狀可以不同。

模板模式的優(yōu)缺點

優(yōu)點:

1、利用模板方法將相同處理邏輯的代碼放到抽象類中,可以提高代碼的復(fù)用性。

2、將不同的代碼在不同的子類中實現(xiàn),通過對子類的擴(kuò)展增加新的行為,提高代碼的擴(kuò)展性。

3、把不變的行為寫在父類上,去除子類的重復(fù)代碼,提供了一個很好的代碼復(fù)用平臺,符合開閉原則。

缺點:

1、類數(shù)目的增加,每一個抽象類都需要至少一個子類來實現(xiàn),這樣導(dǎo)致類的個數(shù)增加,從而導(dǎo)致系統(tǒng)復(fù)雜度的增加。

2、繼承關(guān)系自身的缺點,如果父類添加了新的抽象方法,所有子類都要增加該方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容