【設計模式】【應用】使用模板方法設計模式、策略模式 處理DAO中的增刪改查

原文:使用模板方法設計模式、策略模式 處理DAO中的增刪改查html

關於模板模式策略模式參考前面的文章。java

分析

在dao中,咱們常常要作增刪改查操做,若是每一個對每一個業務對象的操做都寫一遍,代碼量很是龐大.
所以,咱們能夠將dao中增刪改查分開爲兩個部分,
一些是不變的代碼,好比建立局部變量Connection conn,PreparedStatement ps,ResultSet rs等等。算法

public int update(String sql, Object[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DbUtils.getConnection();
            ps = conn.prepareStatement(sql);
            //參數設置
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            DbUtils.close(rs);
            DbUtils.close(ps);
            DbUtils.close(conn);
        }
    }

上面代碼,在每一個操做中基本同樣,咱們能夠將其提取出來。
另一部分是變化的代碼,即咱們傳入的參數如sql,以及參數列表Object[] params等等。
對vo的增刪改查能夠使用下面方法:sql

public int update(String sql, Object[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DbUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++)
                ps.setObject(i + 1, args[i]);
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            DbUtils.close(rs);
            DbUtils.close(ps);
            DbUtils.close(conn);
        }
    }

使用模板模式整改

對vo的查詢關係到對ResultSet的處理,咱們能夠在抽象父類中定義一個抽象的方法。設計模式

abstract Object rowMapper(ResultSet rs) throws SQLException;

這樣子類繼承了抽象父類後必須實現rowMapper這個方法。這樣咱們就能夠構造出一個將方法抽象到一個父類AbstractDao中。app

package com.royzhou.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.royzhou.db.DbUtils;
import com.royzhou.exception.DaoException;

public abstract class AbstractDao {
    
    public Object find(String sql, Object[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DbUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++)
                ps.setObject(i + 1, args[i]);
            rs = ps.executeQuery();
            Object obj = null;
            if (rs.next()) {
                //rowMapper實如今子類中,根據子類算法進行處理
                //只能有一種算法處理,若是要想對rs有不一樣處理要使用下面的策略模式
                obj = rowMapper(rs);
            }
            return obj;
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            DbUtils.close(rs);
            DbUtils.close(ps);
            DbUtils.close(conn);
        }
    }

    abstract protected Object rowMapper(ResultSet rs) throws SQLException;

    public int update(String sql, Object[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DbUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++)
                ps.setObject(i + 1, args[i]);
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            DbUtils.close(rs);
            DbUtils.close(ps);
            DbUtils.close(conn);
        }
    }
}

而後在UserDaoImpl中咱們繼承AbstractDao這個類並實現rowMapper這個抽象方法(子類實現父類定義的抽象方法)。ide

@Override
    protected Object rowMapper(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getString("id"));
        user.setUserName(rs.getString("userName"));
        user.setLoginId(rs.getString("loginId"));
        user.setPassword(rs.getString("password"));
        return user;
    }

這樣dao實現類中的代碼就變得簡單明瞭不少了。完整dao實現類UserDaoImpl代碼以下:this

package com.royzhou.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;

import com.royzhou.dao.AbstractDao;
import com.royzhou.vo.User;

public class UserDaoImpl extends AbstractDao {
    public User findUser(String id) {
        String sql = "select id, userName, loginId, password  from user where id=?";
        Object[] args = new Object[] { id };
        Object user = super.find(sql, args);
        return (User) user;
    }
    
    @Override
    protected Object rowMapper(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getString("id"));
        user.setUserName(rs.getString("userName"));
        user.setLoginId(rs.getString("loginId"));
        user.setPassword(rs.getString("password"));
        return user;
    }

    public void delete(User user) {
        String sql = "delete from user where id=?";
        Object[] args = new Object[] { user.getId() };
        super.update(sql, args);
    }

    public void update(User user) {
        String sql = "update user set userName=?, loginId=?, password=? where id=? ";
        Object[] args = new Object[] { user.getUserName(), user.getLoginId(),
                user.getPassword(), user.getId() };
        super.update(sql, args);
    }
}

使用策略模式整改

不過上面代碼查詢的時候存在一個問題:假如我只須要查找username這一列的數據,咱們必須重寫rowMapper這個實現,並且不方便,程序不夠靈活。rowMapper實現以下:設計

@Override
    protected Object rowMapper(ResultSet rs) throws SQLException {
        return rs.getString("userName");
    }

這顯然不是咱們想要看到的。爲了解決這個問題,咱們能夠使用策略模式來改進咱們的程序。code

GOF《設計模式》一書對Strategy模式是這樣描述的:
定義一系列的算法,把他們一個個封裝起來,而且使它們可相互替換。Strategy模式使算法可獨立於使用它的客戶而變化。

Strategy模式如下列幾條原則爲基礎:
1) 每一個對象都是一個具備職責的個體。
2) 這些職責不一樣的具體實現是經過多態的使用來完成的。
3) 概念上相同的算法具備多個不一樣的實現,須要進行管理。

若是在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式能夠動態的讓一個對象在許多行爲中選擇一種行爲。

若是系統須要動態地在幾種算法中選擇一種。那麼這些算法能夠包裝到一個個的具體算法類裏面,而這些算法類都是一個抽象算法類的子類。換言之,這些具體算法類均有統一的接口,因爲多態性原則。客戶端能夠選擇使用任何一個具體算法類,並只持有一個數據類型是抽象算法類的對象。

設計原則是把一個類中常常改變或者未來可能改變的部分提取出來,做爲一個接口,而後在類中包含這個對象的實例,這樣類的實例在運行時就能夠隨意調用實現了這個接口的類的行爲。

一般,策略模式適用於當一個應用程序須要實現一種特定的服務或者功能,並且該程序有多種實現方式時使用。

在處理ResultSet結果集的時候,咱們能夠使用接口變成,將結果集的操做交給一個接口來處理

public Object find(String sql, Object[] args, RowMapper rowMapper)

其中RowMapper這個接口裏面只有一個方法。

public Object mapRow(ResultSet rs) throws SQLException

用它來處理咱們查詢到的結果集。

package com.royzhou.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.royzhou.db.DbUtils;
import com.royzhou.exception.DaoException;

public class MyDaoTemplate {
    //根據傳入的策略RowMapper來處理結果集ResultSet
    public Object find(String sql, Object[] args, RowMapper rowMapper) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DbUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++)
                ps.setObject(i + 1, args[i]);
            rs = ps.executeQuery();
            Object obj = null;
            if (rs.next()) {
                obj = rowMapper.mapRow(rs);
            }
            return obj;
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            DbUtils.close(rs);
            DbUtils.close(ps);
            DbUtils.close(conn);
        }
    }
}

這樣咱們就能夠根據不一樣的須要使用實現了RowMapper這個接口的類來處理咱們的結果集(在這裏咱們使用的是匿名類) 。

package com.royzhou.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;

import com.royzhou.dao.MyDaoTemplate;
import com.royzhou.dao.RowMapper;
import com.royzhou.vo.User;

public class UserDaoImpl1 {
    private MyDaoTemplate template = new MyDaoTemplate();
    
    public User findUser(String id) {
        String sql = "select id, userName, loginId, password  from user where id=?";
        Object[] args = new Object[] { id };
        Object user = this.template.find(sql, args, new RowMapper(){
            public Object mapRow(ResultSet rs) throws SQLException {
                User user = new User();
                user.setId(rs.getString("id"));
                user.setUserName(rs.getString("userName"));
                user.setLoginId(rs.getString("loginId"));
                user.setPassword(rs.getString("password"));
                return user;
            }
        });
        return (User) user;
    }

    public String findUserName(String id) {
        String sql = "select userName from user where id=?";
        Object[] args = new Object[] {id};
        Object userName = this.template.find(sql, args, new RowMapper(){
            public Object mapRow(ResultSet rs) throws SQLException {
                return rs.getString("userName");
            }
        });
        return (String)userName;
    }
}

通過這樣的修改程序變得更加靈活了。對於不一樣的查詢咱們只須要用相對的策略寫一個匿名類就能夠了。

經過上面例子咱們能夠總結一下策略模式的優缺點:

優勢:
1.能夠很方便的動態改變算法或行爲
2.避免使用多重條件轉移語句

缺點: 1.客戶端必須知道全部的策略類,並自行決定使用哪個策略類。 2.形成不少的策略類(實現類)。

相關文章
相關標籤/搜索