门面模式

一、定义

门面模式(Facade Pattern)又叫外观模式,提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构性模式。其实,在我们日常的编码工作中,经常使用门面模式,但凡只要高层模块需要调度多个子系统(2个以上类对象),我们都会自觉地创建一个新类封装这些子系统,提供精简接口,让高层模块可以更加容易间接调用这些子系统的功能。尤其是现阶段各种第三方SDK,各种开源类库,很大概率都会使用门面模式。 

门面模式主要包含2种角色:

外观角色(Facade):也称门面角色,系统对外的统一接口;

子系统角色(SubSystem):可以同时有一个或多个 SubSystem。每个 SubSytem 都不是一个单独的类,而是一个类的集合。 SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言, Facade 只是另一个客户端而已(即 Facade 对 SubSystem 透明)。

二、门面模式的应用场景

SSM架构就是一个很好的门面模式说明,多个业务的调用封装成一个接口给用户调用;

子系统角色中的类:

public class ModuleA {
    //示意方法
    public void testA(){
        System.out.println("调用ModuleA中的testA方法");
    }
}
public class ModuleB {
    //示意方法
    public void testB(){
        System.out.println("调用ModuleB中的testB方法");
    }
}
public class ModuleC {
    //示意方法
    public void testC(){
        System.out.println("调用ModuleC中的testC方法");
    }
}

门面角色类:

public class Facade {
    //示意方法,满足客户端需要的功能
    public void test(){
        ModuleA a = new ModuleA();
        a.testA();
        ModuleB b = new ModuleB();
        b.testB();
        ModuleC c = new ModuleC();
        c.testC();
    }
}

客户端角色类:

public class Client {
 
    public static void main(String[] args) {
        
        Facade facade = new Facade();
        facade.test();
    }
 
}

Facade类其实相当于A、B、C模块的外观界面,有了这个Facade类,那么客户端就不需要亲自调用子系统中的A、B、C模块了,也不需要知道系统内部的实现细节,甚至都不需要知道A、B、C模块的存在,客户端只需要跟Facade类交互就好了,从而更好地实现了客户端和子系统中A、B、C模块的解耦,让客户端更容易地使用系统。

三、源码中的应用

Spring JDBC模块下的JdbcUtils类,它封装了和JDBC相关的所有操作,像里面的什么closeConnection方法呀,他里面的逻辑调用其实就是个门面模式,他自己啥都没做,事都让Connection 类做了,这里面还有很多方法的调用都这样

public abstract class JdbcUtils {
    public static final int TYPE_UNKNOWN = -2147483648;
    private static final Log logger = LogFactory.getLog(JdbcUtils.class);

    public JdbcUtils() {
    }

    public static void closeConnection(@Nullable Connection con) {
        if (con != null) {
            try {
                con.close();
            } catch (SQLException var2) {
                logger.debug("Could not close JDBC Connection", var2);
            } catch (Throwable var3) {
                logger.debug("Unexpected exception on closing JDBC Connection", var3);
            }
        }

    }

    public static void closeStatement(@Nullable Statement stmt) {
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException var2) {
                logger.trace("Could not close JDBC Statement", var2);
            } catch (Throwable var3) {
                logger.trace("Unexpected exception on closing JDBC Statement", var3);
            }
        }

    }

    public static void closeResultSet(@Nullable ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException var2) {
                logger.trace("Could not close JDBC ResultSet", var2);
            } catch (Throwable var3) {
                logger.trace("Unexpected exception on closing JDBC ResultSet", var3);
            }
        }

    }

    @Nullable
    public static Object getResultSetValue(ResultSet rs, int index, @Nullable Class<?> requiredType) throws SQLException {
        if (requiredType == null) {
            return getResultSetValue(rs, index);
        } else if (String.class == requiredType) {
            return rs.getString(index);
        } else {
            Object value;
            if (Boolean.TYPE != requiredType && Boolean.class != requiredType) {
                if (Byte.TYPE != requiredType && Byte.class != requiredType) {
                    if (Short.TYPE != requiredType && Short.class != requiredType) {
                        if (Integer.TYPE != requiredType && Integer.class != requiredType) {
                            if (Long.TYPE != requiredType && Long.class != requiredType) {
                                if (Float.TYPE != requiredType && Float.class != requiredType) {
                                    if (Double.TYPE != requiredType && Double.class != requiredType && Number.class != requiredType) {
                                        if (BigDecimal.class == requiredType) {
                                            return rs.getBigDecimal(index);
                                        }

                                        if (Date.class == requiredType) {
                                            return rs.getDate(index);
                                        }

                                        if (Time.class == requiredType) {
                                            return rs.getTime(index);
                                        }

                                        if (Timestamp.class != requiredType && java.util.Date.class != requiredType) {
                                            if (byte[].class == requiredType) {
                                                return rs.getBytes(index);
                                            }

                                            if (Blob.class == requiredType) {
                                                return rs.getBlob(index);
                                            }

                                            if (Clob.class == requiredType) {
                                                return rs.getClob(index);
                                            }

                                            if (requiredType.isEnum()) {
                                                Object obj = rs.getObject(index);
                                                if (obj instanceof String) {
                                                    return obj;
                                                }

                                                if (obj instanceof Number) {
                                                    return NumberUtils.convertNumberToTargetClass((Number)obj, Integer.class);
                                                }

                                                return rs.getString(index);
                                            }

                                            try {
                                                return rs.getObject(index, requiredType);
                                            } catch (AbstractMethodError var5) {
                                                logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", var5);
                                            } catch (SQLFeatureNotSupportedException var6) {
                                                logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", var6);
                                            } catch (SQLException var7) {
                                                logger.debug("JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method", var7);
                                            }

                                            String typeName = requiredType.getSimpleName();
                                            if ("LocalDate".equals(typeName)) {
                                                return rs.getDate(index);
                                            }

                                            if ("LocalTime".equals(typeName)) {
                                                return rs.getTime(index);
                                            }

                                            if ("LocalDateTime".equals(typeName)) {
                                                return rs.getTimestamp(index);
                                            }

                                            return getResultSetValue(rs, index);
                                        }

                                        return rs.getTimestamp(index);
                                    }

                                    value = rs.getDouble(index);
                                } else {
                                    value = rs.getFloat(index);
                                }
                            } else {
                                value = rs.getLong(index);
                            }
                        } else {
                            value = rs.getInt(index);
                        }
                    } else {
                        value = rs.getShort(index);
                    }
                } else {
                    value = rs.getByte(index);
                }
            } else {
                value = rs.getBoolean(index);
            }

            return rs.wasNull() ? null : value;
        }
    }

    @Nullable
    public static Object getResultSetValue(ResultSet rs, int index) throws SQLException {
        Object obj = rs.getObject(index);
        String className = null;
        if (obj != null) {
            className = obj.getClass().getName();
        }

        if (obj instanceof Blob) {
            Blob blob = (Blob)obj;
            obj = blob.getBytes(1L, (int)blob.length());
        } else if (obj instanceof Clob) {
            Clob clob = (Clob)obj;
            obj = clob.getSubString(1L, (int)clob.length());
        } else if (!"oracle.sql.TIMESTAMP".equals(className) && !"oracle.sql.TIMESTAMPTZ".equals(className)) {
            if (className != null && className.startsWith("oracle.sql.DATE")) {
                String metaDataClassName = rs.getMetaData().getColumnClassName(index);
                if (!"java.sql.Timestamp".equals(metaDataClassName) && !"oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
                    obj = rs.getDate(index);
                } else {
                    obj = rs.getTimestamp(index);
                }
            } else if (obj instanceof Date && "java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(index))) {
                obj = rs.getTimestamp(index);
            }
        } else {
            obj = rs.getTimestamp(index);
        }

        return obj;
    }

    public static Object extractDatabaseMetaData(DataSource dataSource, DatabaseMetaDataCallback action) throws MetaDataAccessException {
        Connection con = null;

        Object var4;
        try {
            con = DataSourceUtils.getConnection(dataSource);
            DatabaseMetaData metaData = con.getMetaData();
            if (metaData == null) {
                throw new MetaDataAccessException("DatabaseMetaData returned by Connection [" + con + "] was null");
            }

            var4 = action.processMetaData(metaData);
        } catch (CannotGetJdbcConnectionException var10) {
            throw new MetaDataAccessException("Could not get Connection for extracting meta data", var10);
        } catch (SQLException var11) {
            throw new MetaDataAccessException("Error while extracting DatabaseMetaData", var11);
        } catch (AbstractMethodError var12) {
            throw new MetaDataAccessException("JDBC DatabaseMetaData method not implemented by JDBC driver - upgrade your driver", var12);
        } finally {
            DataSourceUtils.releaseConnection(con, dataSource);
        }

        return var4;
    }

    public static <T> T extractDatabaseMetaData(DataSource dataSource, String metaDataMethodName) throws MetaDataAccessException {
        return extractDatabaseMetaData(dataSource, (dbmd) -> {
            try {
                Method method = DatabaseMetaData.class.getMethod(metaDataMethodName, (Class[])null);
                return method.invoke(dbmd, (Object[])null);
            } catch (NoSuchMethodException var3) {
                throw new MetaDataAccessException("No method named '" + metaDataMethodName + "' found on DatabaseMetaData instance [" + dbmd + "]", var3);
            } catch (IllegalAccessException var4) {
                throw new MetaDataAccessException("Could not access DatabaseMetaData method '" + metaDataMethodName + "'", var4);
            } catch (InvocationTargetException var5) {
                if (var5.getTargetException() instanceof SQLException) {
                    throw (SQLException)var5.getTargetException();
                } else {
                    throw new MetaDataAccessException("Invocation of DatabaseMetaData method '" + metaDataMethodName + "' failed", var5);
                }
            }
        });
    }

    public static boolean supportsBatchUpdates(Connection con) {
        try {
            DatabaseMetaData dbmd = con.getMetaData();
            if (dbmd != null) {
                if (dbmd.supportsBatchUpdates()) {
                    logger.debug("JDBC driver supports batch updates");
                    return true;
                }

                logger.debug("JDBC driver does not support batch updates");
            }
        } catch (SQLException var2) {
            logger.debug("JDBC driver 'supportsBatchUpdates' method threw exception", var2);
        }

        return false;
    }

    @Nullable
    public static String commonDatabaseName(@Nullable String source) {
        String name = source;
        if (source != null && source.startsWith("DB2")) {
            name = "DB2";
        } else if ("Sybase SQL Server".equals(source) || "Adaptive Server Enterprise".equals(source) || "ASE".equals(source) || "sql server".equalsIgnoreCase(source)) {
            name = "Sybase";
        }

        return name;
    }

    public static boolean isNumeric(int sqlType) {
        return -7 == sqlType || -5 == sqlType || 3 == sqlType || 8 == sqlType || 6 == sqlType || 4 == sqlType || 2 == sqlType || 7 == sqlType || 5 == sqlType || -6 == sqlType;
    }

    public static String lookupColumnName(ResultSetMetaData resultSetMetaData, int columnIndex) throws SQLException {
        String name = resultSetMetaData.getColumnLabel(columnIndex);
        if (name == null || name.length() < 1) {
            name = resultSetMetaData.getColumnName(columnIndex);
        }

        return name;
    }

    public static String convertUnderscoreNameToPropertyName(@Nullable String name) {
        StringBuilder result = new StringBuilder();
        boolean nextIsUpper = false;
        if (name != null && name.length() > 0) {
            if (name.length() > 1 && name.substring(1, 2).equals("_")) {
                result.append(name.substring(0, 1).toUpperCase());
            } else {
                result.append(name.substring(0, 1).toLowerCase());
            }

            for(int i = 1; i < name.length(); ++i) {
                String s = name.substring(i, i + 1);
                if (s.equals("_")) {
                    nextIsUpper = true;
                } else if (nextIsUpper) {
                    result.append(s.toUpperCase());
                    nextIsUpper = false;
                } else {
                    result.append(s.toLowerCase());
                }
            }
        }

        return result.toString();
    }
}

四、总结

优点:

1、简化了调用过程,无需深入了解子系统,以防给子系统带来风险。

2、减少系统依赖、松散耦合

3、更好地划分访问层次,提高了安全性

4、遵循迪米特法则,即最少知道原则。

缺点:

1、当增加子系统和扩展子系统行为时,可能容易带来未知风险

2、不符合开闭原则

3、某些情况下可能违背单一职责原则。

补充:门面模式其实可以理解为是一种静态代理模式;门面模式的重点是在于封装,静态代理的重点是在于增强功能;

 

git源码:https://gitee.com/TongHuaShuShuoWoDeJieJu/design_pattern.git

posted @ 2021-03-29 22:42  童话述说我的结局  阅读(104)  评论(0编辑  收藏  举报