美文网首页设计模式就该这样学
为什么一定要学习设计模式

为什么一定要学习设计模式

作者: Tom弹架构 | 来源:发表于2021-10-30 00:00 被阅读0次

    先来看一个生活案例,当我们开心时,也许会寻求享乐。在学习设计模式之前,你可能会这样感叹:

    file

    学完设计模式之后,你可能会这样感叹:

    file

    大家对比一下前后的区别,有何感受?

    file

    回到代码中,我们来思考一下,设计模式能解决哪些问题?

    1 写出优雅的代码

    先来看一段我很多年前写的代码。

    
    public void setExammingForm(ExammingForm curForm,String parameters)throws BaseException {
        
         ...
    
          JSONObject jsonObj = new JSONObject(parameters);
          //试卷主键
          if(jsonObj.getString("examinationPaper_id")!= null && (!jsonObj.getString ("examinationPaper_id").equals("")))
             curForm.setExaminationPaper_id(jsonObj.getLong("examinationPaper_id"));
          //剩余时间
          if(jsonObj.getString("leavTime") != null && (!jsonObj.getString("leavTime").equals("")))
             curForm.setLeavTime(jsonObj.getInt("leavTime"));
          //单位主键
          if(jsonObj.getString("organization_id")!= null && (!jsonObj.getString ("organization_id").equals("")))
             curForm.setOrganization_id(jsonObj.getLong("organization_id"));
          //考试主键
          if(jsonObj.getString("id")!= null && (!jsonObj.getString("id").equals("")))
             curForm.setId(jsonObj.getLong("id"));
          //考场主键
          if(jsonObj.getString("examroom_id")!= null && (!jsonObj.getString ("examroom_id").equals("")))
             curForm.setExamroom_id(jsonObj.getLong("examroom_id"));
          //用户主键
          if(jsonObj.getString("user_id")!= null && (!jsonObj.getString("user_id").equals("")))
             curForm.setUser_id(jsonObj.getLong("user_id"));
          //专业代码
          if(jsonObj.getString("specialtyCode")!= null && (!jsonObj.getString ("specialtyCode").equals("")))
             curForm.setSpecialtyCode(jsonObj.getLong("specialtyCode"));
          //报考岗位
          if(jsonObj.getString("postionCode")!= null && (!jsonObj.getString ("postionCode").equals("")))
             curForm.setPostionCode(jsonObj.getLong("postionCode"));
          //报考等级
          if(jsonObj.getString("gradeCode")!= null && (!jsonObj.getString ("gradeCode").equals("")))
             curForm.setGradeCode(jsonObj.getLong("gradeCode"));
          //考试开始时间
          curForm.setExamStartTime(jsonObj.getString("examStartTime"));
          //考试结束时间
          curForm.setExamEndTime(jsonObj.getString("examEndTime"));
          
         ...
    }
    

    优化之后的代码如下。

    
    public class ExammingFormVo extends ExammingForm{
    
       private String examinationPaperId;   //试卷主键
       private String leavTime;             //剩余时间
       private String organizationId;       //单位主键
       private String id;                   //考试主键
       private String examRoomId;           //考场主键
       private String userId;               //用户主键
       private String specialtyCode;        //专业代码
       private String postionCode;          //报考岗位
       private String gradeCode;            //报考等级
       private String examStartTime;        //考试开始时间
       private String examEndTime;          //考试结束时间
    
       ...
    }
    
    public void setExammingForm(ExammingForm form,String parameters)throws BaseException {
       try {
          JSONObject json = new JSONObject(parameters);
          ExammingFormVo  vo = JSONObject.parseObject(json,ExammingFormVo.class);
    
          form = vo;
    
       }catch (Exception e){
          e.printStackTrace();
       }
    
    }
    

    2 更好地重构项目

    平时我们写的代码虽然满足了需求,但往往不利于项目的开发与维护,以下面的JDBC代码为例。

    public void save(Student stu){
        String sql = "INSERT INTO t_student(name,age) VALUES(?,?)";
        Connection conn = null;
        Statement st = null;
        try{
            //1. 加载注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2. 获取数据库连接
            conn=DriverManager.getConnection("jdbc:mysql:///jdbc_demo","root","root");
            //3. 创建语句对象
            PreparedStatement ps=conn.prepareStatement(sql);
            ps.setObject(1,stu.getName());
            ps.setObject(2,stu.getAge());
            //4. 执行SQL语句
            ps.executeUpdate();
            //5. 释放资源
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                if(st != null)
                    st.close();
            }catch(SQLException e){
                e.printStackTrace();
            }finally{
                try{
                    if(conn != null)
                        conn.close();
                }catch(SQLException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    //删除学生信息
    public void delete(Long id){
        String sql = "DELETE  FROM t_student WHERE id=?";
        Connection conn = null;
        Statement st = null;
        try{
            //1. 加载注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2. 获取数据库连接
            conn=DriverManager.getConnection("jdbc:mysql:///jdbc_demo","root","root");
            //3. 创建语句对象
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setObject(1,id);
            //4. 执行SQL语句
            ps.executeUpdate();
            //5. 释放资源
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                if(st != null)
                    st.close();
            }catch(SQLException e){
                e.printStackTrace();
            }finally{
                try{
                    if(conn != null)
                        conn.close();
                }catch(SQLException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    //修改学生信息
    public void update(Student stu){
        String sql = "UPDATE t_student SET name=?,age=? WHERE id=?";
        Connection conn = null;
        Statement st = null;
        try{
            //1. 加载注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2. 获取数据库连接
            conn=DriverManager.getConnection("jdbc:mysql:///jdbc_demo","root","root");
            //3. 创建语句对象
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setObject(1,stu.getName());
            ps.setObject(2,stu.getAge());
            ps.setObject(3,stu.getId());
            //4. 执行SQL语句
            ps.executeUpdate();
            //5. 释放资源
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                if(st != null)
                    st.close();
            }catch(SQLException e){
                e.printStackTrace();
            }finally{
                try{
                    if(conn != null)
                        conn.close();
                }catch(SQLException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    上述代码的功能没问题,但是代码重复得太多,因此可以进行抽取,把重复代码放到一个工具类JdbcUtil里。

    //工具类
    public class JdbcUtil {
        private JdbcUtil() { }
        static {
            //1. 加载注册驱动
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static Connection getConnection() {
            try {
                //2. 获取数据库连接
                return DriverManager.getConnection("jdbc:mysql:///jdbc_demo", "root", "root");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        //释放资源
        public static void close(ResultSet rs, Statement st, Connection conn) {
            try {
                if (rs != null)
                    rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (st != null)
                        st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (conn != null)
                            conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    

    只需要在实现类中直接调用工具类JdbcUtil中的方法即可。

    
    //增加学生信息
    public void save(Student stu) {
        String sql = "INSERT INTO t_student(name,age) VALUES(?,?)";
        Connection conn = null;
        PreparedStatement ps=null;
        try {
            conn = JDBCUtil.getConnection();
            //3. 创建语句对象
            ps = conn.prepareStatement(sql);
            ps.setObject(1, stu.getName());
            ps.setObject(2, stu.getAge());
            //4. 执行SQL语句
            ps.executeUpdate();
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(null, ps, conn);
        }
    
    }
    
    //删除学生信息
    public void delete(Long id) {
        String sql = "DELETE  FROM t_student WHERE id=?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn=JDBCUtil.getConnection();
            //3. 创建语句对象
            ps = conn.prepareStatement(sql);
            ps.setObject(1, id);
            //4. 执行SQL语句
            ps.executeUpdate();
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(null, ps, conn);
        }
    
    }
    
    //修改学生信息
    public void update(Student stu) {
        String sql = "UPDATE t_student SET name=?,age=? WHERE id=?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn=JDBCUtil.getConnection();
            //3. 创建语句对象
            ps = conn.prepareStatement(sql);
            ps.setObject(1, stu.getName());
            ps.setObject(2, stu.getAge());
            ps.setObject(3, stu.getId());
            //4. 执行SQL语句
            ps.executeUpdate();
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(null, ps, conn);
        }
    
    }
    
    public Student get(Long id) {
        String sql = "SELECT * FROM t_student WHERE id=?";
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        PreparedStatement ps=null;
        try {
            conn = JDBCUtil.getConnection();
            //3. 创建语句对象
            ps = conn.prepareStatement(sql);
            ps.setObject(1, id);
            //4. 执行SQL语句
            rs = ps.executeQuery();
            if (rs.next()) {
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Student stu = new Student(id, name, age);
                return stu;
            }
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(rs, ps, conn);
        }
        return null;
    }
    
    public List<Student> list() {
        List<Student> list = new ArrayList<>();
        String sql = "SELECT * FROM t_student ";
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        PreparedStatement ps=null;
        try {
            conn=JDBCUtil.getConnection();
            //3. 创建语句对象
            ps = conn.prepareStatement(sql);
            //4. 执行SQL语句
            rs = ps.executeQuery();
            while (rs.next()) {
                long id = rs.getLong("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Student stu = new Student(id, name, age);
                list.add(stu);
            }
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(rs, ps, conn);
        }
        return list;
    }
    
    

    虽然完成了重复代码的抽取,但数据库中的账号密码等直接显示在代码中,不利于后期账户密码改动的维护。可以建立一个db.propertise文件,用来存储这些信息。

    
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql:///jdbcdemo
    username=root
    password=root
    
    

    只需要在工具类JdbcUtil中获取里面的信息即可。

    
    static {
        //1. 加载注册驱动
        try {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = loader.getResourceAsStream("db.properties");
            p = new Properties();
            p.load(inputStream);
            Class.forName(p.getProperty("driverClassName"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static Connection getConnection() {
        try {
            //2. 获取数据库连接
            return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"),
            p.getProperty("password"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    

    代码抽取到这里,貌似已经完成,但在实现类中,依然存在部分重复代码,在DML操作中,除了SQL和设置值的不同,其他都相同,把相同的部分抽取出来,把不同的部分通过参数传递进来,无法直接放在工具类中。此时,可以创建一个模板类JdbcTemplate,创建一个DML和DQL的模板来对代码进行重构。

    
    //查询统一模板
    public static List<Student> query(String sql,Object...params){
        List<Student> list=new ArrayList<>();
        Connection conn = null;
        PreparedStatement ps=null;
        ResultSet rs = null;
        try {
            conn=JDBCUtil.getConnection();
            ps=conn.prepareStatement(sql);
            //设置值
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1, params[i]);
            }
            rs = ps.executeQuery();
            while (rs.next()) {
                long id = rs.getLong("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Student stu = new Student(id, name, age);
                list.add(stu);
            }
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(rs, ps, conn);
        }
        return list;
    }
    
    实现类直接调用方法即可。
    //增加学生信息
    public void save(Student stu) {
        String sql = "INSERT INTO t_student(name,age) VALUES(?,?)";
        Object[] params=new Object[]{stu.getName(),stu.getAge()};
        JdbcTemplate.update(sql, params);
    }
    
    //删除学生信息
    public void delete(Long id) {
        String sql = "DELETE FROM t_student WHERE id = ?";
        JdbcTemplate.update(sql, id);
    }
    
    //修改学生信息
    public void update(Student stu) {
        String sql = "UPDATE t_student SET name = ?,age = ? WHERE id = ?";
        Object[] params=new Object[]{stu.getName(),stu.getAge(),stu.getId()};
        JdbcTemplate.update(sql, params);
    }
    
    public Student get(Long id) {
        String sql = "SELECT * FROM t_student WHERE id=?";
        List<Student> list = JDBCTemplate.query(sql, id);
        return list.size()>0? list.get(0):null;
    }
    
    public List<Student> list() {
        String sql = "SELECT * FROM t_student ";
        return JDBCTemplate.query(sql);
    }
    
    

    这样重复的代码基本就解决了,但有一个很严重的问题,就是这个程序DQL操作中只能处理Student类和t_student表的相关数据,无法处理其他类,比如Teacher类和t_teacher表。不同的表(不同的对象)应该有不同的列,不同列处理结果集的代码就应该不一样,处理结果集的操作只有DAO自己最清楚。也就是说,处理结果的方法根本就不应该放在模板方法中,应该由每个DAO自己来处理。因此,可以创建一个IRowMapper接口来处理结果集。

    
    public interface IRowMapper {
        //处理结果集
        List rowMapper(ResultSet rs) throws Exception;
    }
    
    

    DQL模板类中调用IRowMapper接口中的handle方法,提醒实现类自己去实现mapping方法。

    
    public static List<Student> query(String sql,IRowMapper rsh, Object...params){
        List<Student> list = new ArrayList<>();
        Connection conn = null;
        PreparedStatement ps=null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //设置值
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1, params[i]);
            }
            rs = ps.executeQuery();
            return rsh.mapping(rs);
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(rs, ps, conn);
        }
        return list ;
    }
    
    

    实现类自己去实现IRowMapper接口的mapping方法,想要处理什么类型的数据在里面定义即可。

    
    public Student get(Long id) {
        String sql = "SELECT * FROM t_student WHERE id = ?";
        List<Student> list = JdbcTemplate.query(sql,new StudentRowMapper(), id);
        return list.size()>0? list.get(0):null;
    }
    public List<Student> list() {
        String sql = "SELECT * FROM t_student ";
        return JdbcTemplate.query(sql,new StudentRowMapper());
    }
    class StudentRowMapper implements IRowMapper{
        public List mapping(ResultSet rs) throws Exception {
            List<Student> list=new ArrayList<>();
            while(rs.next()){
                long id = rs.getLong("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Student stu=new Student(id, name, age);
                list.add(stu);
            }
            return list;
        }
    }
    
    

    到这里为止,实现ORM的关键代码已经大功告成,但是DQL查询不单单要查询学生信息(List类型),还要查询学生数量,这时就要通过泛型来完成。

    
    public interface IRowMapper<T> {
        //处理结果集
        T mapping(ResultSet rs) throws Exception;
    }
    
    public static <T> T query(String sql,IRowMapper<T> rsh, Object...params){
        Connection conn = null;
        PreparedStatement ps=null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //设置值
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1, params[i]);
            }
            rs = ps.executeQuery();
            return rsh.mapping(rs);
            //5. 释放资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(rs, ps, conn);
        }
        return null;
    }
    
    

    StudentRowMapper类的代码如下。

    
    class StudentRowMapper implements IRowMapper<List<Student>>{
        public List<Student> mapping(ResultSet rs) throws Exception {
            List<Student> list=new ArrayList<>();
            while(rs.next()){
                long id = rs.getLong("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Student stu=new Student(id, name, age);
                list.add(stu);
            }
            return list;
        }
    }
    
    

    这样,不仅可以查询List,还可以查询学生数量。

    
    public Long getCount(){
        String sql = "SELECT COUNT(*) total FROM t_student";
        Long totalCount = (Long) JdbcTemplate.query(sql,
                new IRowMapper<Long>() {
                    public Long mapping(ResultSet rs) throws Exception {
                        Long totalCount = null;
                        if(rs.next()){
                            totalCount = rs.getLong("total");
                        }
                        return totalCount;
                    }
                });
        return totalCount;
    }
    
    

    这样,重构设计就已经完成,好的代码能让我们以后维护更方便,因此学会对代码的重构是非常重要的。

    3 经典框架都在用设计模式解决问题

    比如,Spring就是一个把设计模式用得淋漓尽致的经典框架。本书会结合JDK、Spring、MyBatis、Netty、Tomcat、Dubbo等经典框架的源码对设计模式展开分析,帮助大家更好、更深入地理解设计模式在框架源码中的落地。

    【推荐】Tom弹架构:收藏本文,相当于收藏一本“设计模式”的书

    本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!
    如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注『 Tom弹架构 』可获取更多技术干货!

    相关文章

      网友评论

        本文标题:为什么一定要学习设计模式

        本文链接:https://www.haomeiwen.com/subject/asowaltx.html