深入浅出设计模式(六):8.门面模式

门面模式,也称外观模式,用于隐藏复杂系统的内部细节,提供简洁的对外接口。本文介绍了门面模式的原理及在Spring JDBC和Hibernate中的实际应用,通过案例展示了如何在数据库操作和ORM框架中使用门面模式来提高代码的可维护性和可扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

8.门面模式(Facade)

到蛋糕店买蛋糕,不需要知道蛋糕怎么制作,蛋糕房就是蛋糕的门面,屏蔽了制作蛋糕的细节,门面模式又称为外观模式。实际开发中,屏蔽了子模块内部的实现细节,只是将客户端需要的接口提供给客户。

哪里会用到门面模式

  • 网上商城有银行支付功能,而银行肯定不能将后台的数据库直接开放给网上商城使用,不同银行提供相应的支付接口,这样的支付接口对于银行而言就是一个门面。

  • 薪资系统开发中,计算每个月薪酬时,要获取很多数据库信息,如果每个员工都这样到每个不同子模块表去获取,则暴露细节太多,如果后台表变化,则需要修改薪资计算的代码,明显不符合松耦合的设计原则,因此需要门面模式,屏蔽细节。

  • 在框架设计中,比如Spring、Struts、Hibernate等,很多地方需要读取xml文件,此时就应该把读取xml配置文件的代码抽取出来,形成统一的接口。

比如对于薪酬模块:

这里写图片描述

代码比较简单,就不在此贴出,全部放在项目中。

门面模式的实现原理

这里写图片描述

门面模式在Spring JDBC中的实际应用

软件开发一般需要对数据库进行操作,直接使用JAVA提供的JDBC往往会比较麻烦,因此需要对JAVA提供的JDBC进行封装,提供一个公用的工具类,这就是门面模式。已有的工具类如Hibernate、Spring JDBC等。

先自己写一个封装的JDBC工具类:

JdbcUtil

import java.io.InputStream;
import java.io.Reader; 
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.rowset.CachedRowSet;
import com.sun.rowset.CachedRowSetImpl; 

public class JdbcUtil {
   
   
    private Connection conn;
    private PreparedStatement pstmt = null;
    private Statement stmt = null;
    private boolean isStmt = true;
    private String sql = "";

    public JdbcUtil() {
    }

    public JdbcUtil(Connection conn) {
        this.conn = conn;
    }

    // 使用PreparedStatement
    public void setPst() {
        this.isStmt = false;
    }

    /**
     * @description 执行传入的sql 
     * @date  2016年1月13日
     */
    public int executeUpdate(String sql) throws SQLException {
        this.sql = sql;
        int numRow = 0;
        stmt = null;
        pstmt = null;
        // 判断连接是否为空或已关闭
        if (conn == null || conn.isClosed())
            throw new SQLException("请首先创建或获取一个连接");
        // 判断传进来的sql是否为空
        if (sql == null || "".equals(sql.trim()))
            throw new SQLException("sql为null");
        try {
            // 判断是否使用Statement
            if (isStmt) {
  
  // 使用Statement
                synchronized (this.conn) {
  
  // 用来保证一个对象在多个线程中访问该方法时是线程安全的
                    stmt = this.conn.createStatement();
                }
                numRow = stmt.executeUpdate(sql);// 返回执行成功的笔数
                if (stmt != null) {
                    stmt.close();
                }
            } 
            else {
  
  // 使用PreparedStatement
                synchronized (this.conn) {
  
  // 用来保证一个对象在多个线程中访问该方法时是线程安全的
                    pstmt = this.conn.prepareStatement(sql);
                }
                numRow = pstmt.executeUpdate();// 返回执行成功的笔数
                if (pstmt != null) {
                    pstmt.close();
                }
            }
        } 
        catch (SQLException e) {
            throw new SQLException("执行executeUpdate失败" + sql + e);
        } 
        finally {
            return numRow;
        }
    }

    /**
     * @description 根据sql语句返回单笔数据结果集 
     * @date  2016年1月13日
     */
    public ResultSet getResultSet(String sql) throws SQLException {
        ResultSet rs = null;
        try {
            rs = this.getAllResultSet(sql, 1);//通过getAllResultSet()方法实现ResultSet
        } 
        catch (SQLException e) {
            throw new SQLException("执行getResultSet失败" + sql + e);
        } 
        finally {
            return rs;
        }
    }

    /**
     * @description 根据sql语句和数量数 返回结果集(limit=1时即单笔)
     * @date  2016年1月13日
     */
    public ResultSet getAllResultSet(String sql, int limit) throws SQLException {
        this.sql = sql;
        stmt = null;
        pstmt = null;
        ResultSet rs = null;
        //判断连接是否为空或已关闭
        if (conn == null || conn.isClosed())
            throw new  SQLException("请首先创建或获取一个连接");
        //判断传进来的sql是否为空
        if (sql == null || "".equals(sql.trim()))
            throw new  SQLException("sql为null");
        try { 
            //判断是否使用Statement
            if (isStmt) {
  
  //使用Statement
                synchronized (this.conn) {
  
  //用来保证一个对象在多个线程中访问该方法时是线程安全的
                    stmt = this.conn.createStatement();
                }
                if (limit > 0) {
                    stmt.setMaxRows(limit);
                }
                rs = stmt.executeQuery(sql);
            } 
            else {
  
  //使用PreparedStatement
                this.PreparedStatement(sql, limit);//通过PreparedStatement()方法生成pstmt
                rs = pstmt.executeQuery();
            }
        } 
        catch (SQLException e) {
            throw new SQLException("执行getAllResultSet失败" + sql + e);
        } 
        finally {
            return rs;
        }
    }

    /**
     * @description 用来获取单笔数据结果集,将ResultSet转成Map形式 
     * @date  2016年1月13日
     */
    public Map queryMap(String sql) throws SQLException {
        Map map = null;
        try {
            //单笔通过多笔实现
            Map[] maps = this.queryAllMap(sql, 1);
            if (maps != null && maps.length == 1) {
                map = maps[0];//只取一笔
            }
        } 
        catch (SQLException e) {
            throw new SQLException("执行queryMap失败" + sql + e);
        } 
        finally {
            return map;  
        }
    }

    /**
     * @description 用来获取多笔数据结果集 ,将ResultSet转成Map形式  
     * @date  2016年1月13日
     */
    public Map[] queryAllMap(String sql, int limit) throws SQLException {
        Map[] map = null;
        CachedRowSet rs = null;
        List list = new ArrayList();
        try {
            //通过getAllCachedRowSet获取rs,然后循环将每笔转换为map
            for (rs = this.getAllCachedRowSet(sql, limit); rs.next();) {
                list.add(getMapFromRs(rs));//将每笔转换为map
            }
        } 
        catch (SQLException e) {
            throw new SQLException("执行queryAllMap失败" + sql + e);
        } 
        finally {
            if (rs != null) {
                rs.close();//关闭rs
            }
            if (list.size() != 0) {
                map = new Map[list.size()];
                list.toArray(map);//将list转换为map数组
            }
            return map;  
        }
    }

    /**
     * @description  用来获取单笔数据结果集,希望返回的是对象,可以转换为Javabean  
     * @date  2016年1月13日
     */
    public Object queryObj(String sql) throws SQLException {
        Object obj = null;
        try {
            //单笔通过多笔实现
            List list = this.queryAllObj(sql, 1);
            if (list != null && list.size() == 1) {
                obj = list.get(0);//只取一笔
            }
        } 
        catch (SQLException e) {
            throw new SQLException("执行queryObj失败" + sql + e);
        } 
        finally {
            return obj;  
        }
    }

    /**
     * @description 用来获取多笔数据结果集,希望返回的是对象,可以转换为Javabean  
     * @date  2016年1月13日
     */
    public List queryAllObj(String sql, int limit) throws SQLException {
        CachedRowSet rs = null;
        List list = null;
        try {
            list = new ArrayList();
            //通过getAllCachedRowSet,取得rs,然后循环获取每个Object
            for (rs = this.getAllCachedRowSet(sql, limit); rs.next();) {
                list.add(getObjFromRs(rs));//获取每个Object,将其存入list
            }
        } catch (SQLException e) {
            throw new SQLException("执行queryAllObj失败" + sql + e);
        } finally {
            if (rs != null) {
                rs.close();//关闭rs
            }
            return list;  
        }
    }

    public CachedRowSet getAllCachedRowSet(String sql, int limit) throws SQLException {
        CachedRowSetImpl crs = null;
        try {
            crs = new CachedRowSetImpl();
            crs.populate(this.getAllResultSet(sql, limit));//通过getAllResultSet()方法实现CachedRowSet
        } catch (SQLException e) {
            e.printStackTrace();
            throw new SQLException("执行getAllCachedRowSet失败" + sql + e);
        } finally {
            return crs;
        } 
    }

    /**
     * @description 将获取的栏位名称和栏位内容相对应, 
     * @date  2016年1月13日
     */
    private Map getMapFromRs(CachedRowSet rs) throws SQLException  {
        Map map = new HashMap();
        List columnNamesList = new ArrayList();
        int columnCount = 0;
        try {
            columnCount = rs.getMetaData().getColumnCount();//获取ResultSetMetaData的字段数目
            columnNamesList = setColumnNameByMeta(rs.getMetaData());//获取每个字段的名称
            for (int i = 0; i < columnCount; i++) {
                map.put((String) columnNamesList.get(i), rs.getString(i + 1));//将字段名和对应的值存入Map
            }
        } catch (SQLException e) {
            throw new SQLException("执行getMapFromRS失败" + e);
        } finally {
            return map;
        }
    }

    /**
     * @description 该方法用来将获取的栏位名称和栏位内容相对应,可以由继承它的类实现,这样可以与VO相结合。
     * @date  2016年1月13日
     */ 
    protected Object getObjFromRs(ResultSet rs) throws SQLException  {
        /*//获取infoIn中存储的在Xml中设定的对应信息
        List voId = (infoIn.get("voId") == null) ? new ArrayList() : (List)infoIn.get("voId");//获取Xml中设定的voId
        List voClass = (infoIn.get("voClass") == null) ? new ArrayList() : (List)infoIn.get("voClass");//获取Xml中设定的voClass
        List voType = (infoIn.get("voType") == null) ? new ArrayList() : (List)infoIn.get("voType");//获取Xml中设定的voType
        List voTable = (infoIn.get("voTable") == null) ? new ArrayList() : (List)infoIn.get("voTable");//获取Xml中设定的voValidate
        //确定开发人员设定sql中的表名,这里只对第一个表中的值转换为VO
        int startPos = sql.toUpperCase().indexOf("FROM") + 4;//取得第一个from中m的后一个字符的位置
        String strStart = sql.substring(startPos).trim();//取得从第一个表名后的sql字符串的值
        char[] sqlStr = strStart.toCharArray();//将其转换为char[]
        int endPos = 0;
        //对从第一个表名后的sql字符串的值进行一个一个的循环
        for (int i = 0; sqlStr != null && sqlStr.length > i; i++) {
            //如果从第一个表名后的sql字符串的值中有空格或,则停止循环,并记录此时的位置,则之前的字符串即为表名
            if (" ".equals(String.valueOf(sqlStr[i])) || ",".equals(String.valueOf(sqlStr[i]))) {
                endPos = i;
                break;
            } else {
                endPos = i + 1;//如果整个从第一个表名后的sql字符串的值都为表名
            }
        }
        String classVo = "";
        String table = strStart.substring(0, endPos).toUpperCase();//取得表名,并转换为大写
        //和XMl中设定的表名进行对比
        for (int i = 0; voId.size() > i; i++) {
            String strTable = voTable.get(i) == null ? "" : (String)voTable.get(i);
            if (table.equals(strTable.toUpperCase())) {//如果该sql中的表名和Xml中的相对应,则去对该表对应的Class,即VO
                classVo = (String)voClass.get(i);
            }
        }
        Object obj = null;
        List columnNamesList = new ArrayList();
        int columnCount = 0;
        try {
            columnCount = rs.getMetaData().getColumnCount();//获取ResultSetMetaData的字段数目
            columnNamesList = setColumnNameByMeta(rs.getMetaData());//获取每个字段的名称
            Class cls = Class.forName(classVo); 
            obj = (Object)cls.newInstance();//取得该Class即VO的实例
            Method[] mtdVos = cls.getMethods();//获取该VO中的所有方法
            for (int i = 0; i < columnCount; i++) {
                //对该VO中的所有方法进行循环,如果方法名和表中的字段名一致,且该方法为set方法,则将值注入
                for (int m = 0; mtdVos.length > m; m++) {
                    if ("set".equals(mtdVos[m].getName().substring(0, 3)) && ((String) columnNamesList.get(i)).toUpperCase().equals(mtdVos[m].getName().substring(3).toUpperCase())) {
                        Method mtdVo = cls.getMethod(mtdVos[m].getName(), new Class[]{String.class});
                        mtdVo.invoke(obj, rs.getString(i + 1));//将数据库中的值注入VO
                    }
                }
            }
        } catch (SQLException e) {
            throw new SQLException("执行getObjFromRS失败" + e);
        } finally {
            return obj;
        }*/
        return null;
    }

    /**
     * @description 获取处理数据库中的栏位名和栏位数目
     * @param rsMetadata
     * @return
     * @throws SQLException
     * @author 刘泉 QQ:3262332995
     * @date  2016年1月13日
     */
    private List setColumnNameByMeta(ResultSetMetaData rsMetadata) throws SQLException {
        List columnNamesList = new ArrayList();
        try {
            //获取ResultSetMetaData的字段数目
            for (int i = 0; i < rsMetadata.getColumnCount(); i++) {
                columnNamesList.add(rsMetadata.getColumnName(i + 1));//获取每个字段的名称
            } 
        } catch (SQLException e) {
            throw new SQLException("执行setColumnNameByMeta失败" + e);
        } finally {
            return columnNamesList;
        }
    }

    /**
     * @description  用来通过PreparedStatement设定数据结果集
     * @date  2016年1月13日
     */
    public void PreparedStatement(String sql) throws  SQLException{
        this.PreparedStatement(sql, 0);
    }

    /**
     * @description  用来通过PreparedStatement设定多笔数据结果集
     * @date  2016年1月13日
     */
    public void PreparedStatement(String sql, int limit) throws  SQLException{
        this.PreparedStatement(sql, limit, 0);
    }

    /**
     * @description 用来通过PreparedStatement设定多笔数据结果集
     * @param sql
     * @param limit 用来限制查询笔数,<=0则代表不限制
     * @param gk 用来自动生成主键值,>0表示自动生成主键  
     * @date  2016年1月13日
     */
    public void PreparedStatement(String sql, int limit, int gk) throws  SQLException{
        this.sql = sql;
        pstmt = null;
//      判断连接是否为空或已关闭
        if (conn == null || conn.isClosed())
            throw new  SQLException("请首先创建或获取一个连接");
        //判断传进来的sql是否为空
        if (sql == null || "".equals(sql.trim()))
            throw new  SQLException("sql为null");
        try {
            synchronized (this.conn) {
  
  //用来保证一个对象在多个线程中访问该方法时是线程安全的
                if (gk > 0) {
                    pstmt = this.conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);//自动生成主键
                } else {
                    pstmt = this.conn.prepareStatement(sq
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值