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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 先来看一个生活案例,当我们开心时,也许会寻求享乐。在学习设计模式之前,你可能会这样感叹:

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

20211030000053640.png

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

20211030000053876.png

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

20211030000054358.png

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

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弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦


本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
5月前
|
设计模式 存储 Java
认真学习设计模式之观察者模式(Observer Pattern)
认真学习设计模式之观察者模式(Observer Pattern)
31 0
|
7月前
|
设计模式 架构师 Java
阿里P8架构师都要学习研究的java加强版23种设计模式神级PDF文档
说在前面的话 Java作为老牌纯正的编程语言,在规范性上有着天然优势。因此本版的设计模式讲解全部用Java语言来描述,并针对Java语言的特性对讲解内容做了相当大的改动。 不知道大家是否听过编程界的一段话:掌握设计模式相当于华山派的"气宗",是程序员的内功修为,虽然在同样的学习时间下,类似Python这种"剑宗"的开发模式见效更快,但是长远来看,"气宗"才是走向软件架构师以上级别的必由之路。 所以,掌握气宗就掌握了编程命脉,然而学习设计模式有四大境界: 接下来给大家分享的就是java溢彩加强版大话设计模式包含的内容知识点。 总目录 主要内容 本文是百万销量的经典畅销书《
125 0
|
6月前
|
设计模式 缓存 Java
认真学习设计模式之建造者模式(Builder Pattern)
认真学习设计模式之建造者模式(Builder Pattern)
60 1
|
4月前
|
设计模式 监控 安全
多线程设计模式【多线程上下文设计模式、Guarded Suspension 设计模式、 Latch 设计模式】(二)-全面详解(学习总结---从入门到深化)
多线程设计模式【多线程上下文设计模式、Guarded Suspension 设计模式、 Latch 设计模式】(二)-全面详解(学习总结---从入门到深化)
62 0
|
7天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
5月前
|
设计模式 存储 Java
认真学习设计模式之命令模式(Command Pattern)
认真学习设计模式之命令模式(Command Pattern)
81 0
|
4月前
|
设计模式 安全 Java
多线程设计模式【线程安全、 Future 设计模式、Master-Worker 设计模式 】(一)-全面详解(学习总结---从入门到深化)
多线程设计模式【线程安全、 Future 设计模式、Master-Worker 设计模式 】(一)-全面详解(学习总结---从入门到深化)
28 0
|
5月前
|
设计模式 Java 关系型数据库
认真学习设计模式之适配器模式(Adapter Pattern)/包装器模式
认真学习设计模式之适配器模式(Adapter Pattern)/包装器模式
62 0
|
5月前
|
设计模式 Java 数据库连接
认真学习设计模式之外观模式(Facade Pattern)
认真学习设计模式之外观模式(Facade Pattern)
28 0
|
5月前
|
设计模式 Java 应用服务中间件
认真学习设计模式之职责链模式((Chain of Responsibility Pattern)
认真学习设计模式之职责链模式((Chain of Responsibility Pattern)
63 0