设计模式-行为型-中介者模式

254 阅读2分钟

定义

大话设计模式-中介者模式(Mediator):
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。

上面是正式的术语描述,其实就是一个媒介的作用,比如卖房的中介,联合国理事会等都是这样一个作用,在日常生活中很常见。

下面使用 ORM 框架 MyBatis 来学习下这个模式,但是更多的是一个 MyBatis 的源码入门学习。

案例

原始实现

  • 手动与数据库交互,需要指定是什么数据库
  • 处理返回数据比较麻烦,需要自己手动一个个获取
  • 除了真正实现业务的 SQL ,其他都是我们不关系的。而真正 SQL 只占了一行代码。
public class JdbcUtil {
    private static Logger logger = LoggerFactory.getLogger(JdbcUtil.class);
    private static String url = "jdbc:mysql://localhost:3306/apimgt_db_test";
    private static String username = "root";
    private static String password = "root";
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取数据库连接
        Connection connection = DriverManager.getConnection(url, username, password);
        //3.操作数据库
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery("select * from access_api_log limit 1");
        while (resultSet.next()) {
            logger.info("获取主键值,{}", resultSet.getLong("id"));
        }
    }
}

中介者模式

  1. 实现对数据库连接的封装
  2. 实现对返回数据的封装

可以说这是对 MyBatis 的超级简化版代码。

解析 xml 配置文件

我们这里使用的 dom4j jar 包。现在MyBatis 使用的解析 MXL 文件都是 java 自带的 org.w3c.dom 工具包,以及自己衍生的一些类函数。

// mybatis-config-datasource.xml
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="DRUID">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/apimgt_db_test?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/User_Mapper.xml"/>
    </mappers>
</configuration>


// Mapper.xml 文件
 <mapper namespace="com.practice.mybatislearn.dao.IUserDao">
    <select id="queryUserInfoById" parameterType="java.lang.Long" >
        SELECT *
        FROM access_api_log
        limit 1
    </select>
</mapper>   

解析方法执行语句

  • 一个方法解析 xml 配置文件获得数据库连接。
  • 一个方法解析 mapper 文件获取 SQL 语句
  • 最终结合起来执行并返回封装结果封装
public ResultSet parse() {
        try {
            //环境,连接数据库
            Connection connection = environmentsElement(root.element("environments"));
            //解析映射器,获取 sql 语句
            String sql = mapperElement(root.element("mappers"));

            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            ResultSet resultSet = preparedStatement.executeQuery();
            return resultSet;

        } catch (Exception e) {
            throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause:" + e, e);
        }

    }

封装结果

这里是对结果的简单封装,就只是封装为了一个 Map 。真正的 MyBatis 功能非常丰富,可以映射为 Bean 等

 static Map resultSet2Obj(ResultSet resultSet) {
        Map map = new HashMap();
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            //每次遍历行值
            while (resultSet.next()) {
                for (int i = 1; i < columnCount; i++) {
                    Object value = resultSet.getObject(i);
                    String columnName = metaData.getColumnName(i);
                    map.put(columnName, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

调用

我们只需要配置好我们的配置接口,然后 XMLBuilder 类:

  1. 解析配置类获取数据库连接
  2. 获取 sql 语句
  3. 执行语句返回结果
public class ApiTest {
    public static void main(String[] args) throws SQLException {

        XMLBuilder xmlBuilder = new XMLBuilder();
        ResultSet resultSet = xmlBuilder.parse();

        Map result = ResultObject.resultSet2Obj(resultSet);
        System.out.println("结果为:"+result);

    }
}
  1. 我们最后在对结果进行封装,返回到我们想要的格式,而不是我们自己手动的去解析 ResultSet 结果集。

总结

  • 上面的超级简化的 ORM 框架对数据库连接、SQL 执行、结果返回都进行了封装,我们只需要配置好信息即可。
  • 虽然上面的看起来更像是两个函数的拼接,但是大概思路是这样的。这种设计模式满足单一职责、开闭原则,内部实现对客户端隐藏。外部人员只需要调用即可

具体代码可参考:gitee.com/ncharming/k…