設計模式詳解--模板方法模式及適配器模式

模板方法模式

模板模式通常又叫模板方法模式(Template Method Pattern)是指定義一個算法的骨架,並允許子類爲一個或者多個步驟提供實現。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法的某些步驟,屬於行爲性設計模式。模板方法適用於以下應
用場景:

1、一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現。
2、各子類中公共的行爲被提取出來並集中到一個公共的父類中,從而避免代碼重複。

利用模板模式重構 JDBC 操作業務場景

創建一個模板類 JdbcTemplate,封裝所有的 JDBC 操作。以查詢爲例,每次查詢的表不同,返回的數據結構也就不一樣。我們針對不同的數據,都要封裝成不同的實體對象。而每個實體封裝的邏輯都是不一樣的,但封裝前和封裝後的處理流程是不變的,因此,我們可以使用模板方法模式來設計這樣的業務場景。

1)先創建約束 ORM 邏輯的接口RowMapper:
 

/**
 * ORM映射定製化的接口
 */
public interface RowMapper<T> {
    T mapRow(ResultSet rs,int rowNum) throws Exception;
}

2)再創建封裝了所有處理流程的抽象類 JdbcTemplate:

public abstract class JdbcTemplate {
    private DataSource dataSource;

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

    public List<?> executeQuery(String sql, RowMapper<?> rowMapper, Object[] values){
        try {
            //1、獲取連接
            Connection conn = this.getConnection();
            //2、創建語句集
            PreparedStatement pstm = this.createPrepareStatement(conn,sql);
            //3、執行語句集
            ResultSet rs = this.executeQuery(pstm,values);
            //4、處理結果集
            List<?> result = this.paresResultSet(rs,rowMapper);
            //5、關閉結果集
            this.closeResultSet(rs);
            //6、關閉語句集
            this.closeStatement(pstm);
            //7、關閉連接
            this.closeConnection(conn);
            return result;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    protected void closeConnection(Connection conn) throws Exception {
        //數據庫連接池,我們不是關閉
        conn.close();
    }

    protected void closeStatement(PreparedStatement pstm) throws Exception {
        pstm.close();
    }

    protected void closeResultSet(ResultSet rs) throws Exception {
        rs.close();
    }

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

    protected ResultSet executeQuery(PreparedStatement pstm, Object[] values) throws Exception {
        for (int i = 0; i < values.length; i++) {
            pstm.setObject(i,values[i]);
        }
        return pstm.executeQuery();
    }

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

    public Connection getConnection() throws Exception {
        return this.dataSource.getConnection();
    }
}

 

3)創建實體對象 Member 類:

public class Member {

    private String username;
    private String password;
    private String nickname;
    private int age;
    private String addr;

    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 getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public int getAge() {
        return age;
    }

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

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

4)創建數據庫操作類 MemberDao:

public class MemberDao extends JdbcTemplate {
    public MemberDao(DataSource dataSource) {
        super(dataSource);
    }

    public List<?> selectAll(){
        String sql = "select * from t_member";
        return super.executeQuery(sql, new RowMapper<Member>() {
            public Member mapRow(ResultSet rs, int rowNum) throws Exception {
                Member member = new Member();
                //字段過多,原型模式
                member.setUsername(rs.getString("username"));
                member.setPassword(rs.getString("password"));
                member.setAge(rs.getInt("age"));
                member.setAddr(rs.getString("addr"));
                return member;
            }
        },null);
    }
}

5)客戶端測試代碼:

public class MemberDaoTest {

    public static void main(String[] args) {
        MemberDao memberDao = new MemberDao(null);
        List<?> result = memberDao.selectAll();
        System.out.println(result);
    }
}

當然我們是沒有鏈接的跑不起來的。

6)模板模式的優缺點
優點:
1、利用模板方法將相同處理邏輯的代碼放到抽象父類中,可以提高代碼的複用性。
2、將不同的代碼不同的子類中,通過對子類的擴展增加新的行爲,提高代碼的擴展性。
3、把不變的行爲寫在父類上,去除子類的重複代碼,提供了一個很好的代碼複用平臺,
符合開閉原則。
缺點:
1、類數目的增加,每一個抽象類都需要一個子類來實現,這樣導致類的個數增加。
2、類數量的增加,間接地增加了系統實現的複雜度。
3、繼承關係自身缺點,如果父類添加新的抽象方法,所有子類都要改一遍。
模板方法模式比較簡單,相信小夥伴們肯定能學會,也肯定能理解好!只要勤加練習,多結合業務場景思考問題,就能夠把模板方法模式運用好。

適配器模式

適配器模式的應用場景

適配器模式(Adapter Pattern)是指將一個類的接口轉換成客戶期望的另一個接口,使原本的接口不兼容的類可以一起工作,屬於結構型設計模式。
適配器適用於以下幾種業務場景:
1、已經存在的類,它的方法和需求不匹配(方法結果相同或相似)的情況。
2、適配器模式不是軟件設計階段考慮的設計模式,是隨着軟件維護,由於不同產品、不同廠家造成功能類似而接口不相同情況下的解決方案。有點亡羊補牢的感覺。
生活中也非常的應用場景,例如電源插轉換頭、手機充電轉換頭、顯示器轉接頭。
 

在中國民用電都是 220V 交流電,但我們手機使用的鋰電池使用的 5V 直流電。因此,我們給手機充電時就需要使用電源適配器來進行轉換。下面我們用代碼來還原這個生活場景。

1)創建 AC220 類,表示 220V 交流電:

public class AC220 {

    public int outputAC220V(){
        int output = 220;
        System.out.println("輸出電流" + output + "V");
        return output;
    }
}

2)創建 DC5 接口,表示 5V 直流電的標準:

public interface DC5 {
    int outoupDC5V();
}

3)創建電源適配器 PowerAdapter 類:

public class PowerAdapter implements DC5 {

   private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    public int outoupDC5V() {
        int adapterInput = ac220.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("使用PowerAdapter輸入AC:" + adapterInput + "V,輸出DC:" + adapterOutput + "V");
        return adapterOutput;
    }
}


4)測試類

public class PowerAdapterTest {
    public static void main(String[] args) {
        DC5 dc5 = new PowerAdapter(new AC220());
        dc5.outoupDC5V();
    }
}

5)結果截圖

 

6)適配器模式的優缺點
優點:
1、能提高類的透明性和複用,現有的類複用但不需要改變。
2、目標類和適配器類解耦,提高程序的擴展性。
3、在很多業務場景中符合開閉原則。
缺點:
1、適配器編寫過程需要全面考慮,可能會增加系統的複雜性。
2、增加代碼閱讀難度,降低代碼可讀性,過多使用適配器會使系統代碼變得凌亂。
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章