Why do you have to learn design patterns

Let's take a life case first. When we are happy, we may seek pleasure. Before learning design patterns, you might sigh:

After learning design patterns, you may sigh:

How do you feel about the difference between before and after?

Back in the code, let's think about what design patterns can solve?

1 Write elegant code

Let's start with a piece of code I wrote many years ago.

public void setExammingForm(ExammingForm curForm,String parameters)throws BaseException {
    
     ...

      JSONObject jsonObj = new JSONObject(parameters);
      //Test paper primary key
      if(jsonObj.getString("examinationPaper_id")!= null && (!jsonObj.getString ("examinationPaper_id").equals("")))
         curForm.setExaminationPaper_id(jsonObj.getLong("examinationPaper_id"));
      //Remaining time
      if(jsonObj.getString("leavTime") != null && (!jsonObj.getString("leavTime").equals("")))
         curForm.setLeavTime(jsonObj.getInt("leavTime"));
      //Company PK
      if(jsonObj.getString("organization_id")!= null && (!jsonObj.getString ("organization_id").equals("")))
         curForm.setOrganization_id(jsonObj.getLong("organization_id"));
      //Exam primary key
      if(jsonObj.getString("id")!= null && (!jsonObj.getString("id").equals("")))
         curForm.setId(jsonObj.getLong("id"));
      //Examination room primary key
      if(jsonObj.getString("examroom_id")!= null && (!jsonObj.getString ("examroom_id").equals("")))
         curForm.setExamroom_id(jsonObj.getLong("examroom_id"));
      //User primary key
      if(jsonObj.getString("user_id")!= null && (!jsonObj.getString("user_id").equals("")))
         curForm.setUser_id(jsonObj.getLong("user_id"));
      //Professional code
      if(jsonObj.getString("specialtyCode")!= null && (!jsonObj.getString ("specialtyCode").equals("")))
         curForm.setSpecialtyCode(jsonObj.getLong("specialtyCode"));
      //Post registration
      if(jsonObj.getString("postionCode")!= null && (!jsonObj.getString ("postionCode").equals("")))
         curForm.setPostionCode(jsonObj.getLong("postionCode"));
      //Registration level
      if(jsonObj.getString("gradeCode")!= null && (!jsonObj.getString ("gradeCode").equals("")))
         curForm.setGradeCode(jsonObj.getLong("gradeCode"));
      //Test start time
      curForm.setExamStartTime(jsonObj.getString("examStartTime"));
      //Test end time
      curForm.setExamEndTime(jsonObj.getString("examEndTime"));
      
	 ...
}

The optimized code is as follows.

public class ExammingFormVo extends ExammingForm{

   private String examinationPaperId;	//Test paper primary key
   private String leavTime;				//Remaining time
   private String organizationId;		//Company PK
   private String id;					//Exam primary key
   private String examRoomId;			//Examination room primary key
   private String userId;				//User primary key
   private String specialtyCode;		//Professional code
   private String postionCode;			//Post registration
   private String gradeCode;			//Registration level
   private String examStartTime;		//Test start time
   private String examEndTime;			//Test end time

   ...
}

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 better refactor the project

Although the code we write usually meets the requirements, it is often not conducive to the development and maintenance of the project. Take the following JDBC code as an example.

public void save(Student stu){
    String sql = "INSERT INTO t_student(name,age) VALUES(?,?)";
    Connection conn = null;
    Statement st = null;
    try{
        //1. Load the registered driver
        Class.forName("com.mysql.jdbc.Driver");
        //2. Get database connection
        conn=DriverManager.getConnection("jdbc:mysql:///jdbc_demo","root","root");
        //3. Create statement object
        PreparedStatement ps=conn.prepareStatement(sql);
        ps.setObject(1,stu.getName());
        ps.setObject(2,stu.getAge());
        //4. Execute SQL statement
        ps.executeUpdate();
        //5. Release resources
    }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();
            }
        }
    }
}

//Delete student information
public void delete(Long id){
    String sql = "DELETE  FROM t_student WHERE id=?";
    Connection conn = null;
    Statement st = null;
    try{
        //1. Load the registered driver
        Class.forName("com.mysql.jdbc.Driver");
        //2. Get database connection
        conn=DriverManager.getConnection("jdbc:mysql:///jdbc_demo","root","root");
        //3. Create statement object
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setObject(1,id);
        //4. Execute SQL statement
        ps.executeUpdate();
        //5. Release resources
    }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();
            }
        }
    }
}

//Modify student information
public void update(Student stu){
    String sql = "UPDATE t_student SET name=?,age=? WHERE id=?";
    Connection conn = null;
    Statement st = null;
    try{
        //1. Load the registered driver
        Class.forName("com.mysql.jdbc.Driver");
        //2. Get database connection
        conn=DriverManager.getConnection("jdbc:mysql:///jdbc_demo","root","root");
        //3. Create statement object
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setObject(1,stu.getName());
        ps.setObject(2,stu.getAge());
        ps.setObject(3,stu.getId());
        //4. Execute SQL statement
        ps.executeUpdate();
        //5. Release resources
    }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();
            }
        }
    }
}

The function of the above code is OK, but the code is repeated too much, so it can be extracted and put the repeated code into a tool class JdbcUtil.

//Tool class
public class JdbcUtil {
    private JdbcUtil() { }
    static {
        //1. Load the registered driver
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        try {
            //2. Get database connection
            return DriverManager.getConnection("jdbc:mysql:///jdbc_demo", "root", "root");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //Release resources
    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();
                }
            }
        }
    }
}

Just call the method in the tool class JdbcUtil directly in the implementation class.

//Add student information
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. Create statement object
        ps = conn.prepareStatement(sql);
        ps.setObject(1, stu.getName());
        ps.setObject(2, stu.getAge());
        //4. Execute SQL statement
        ps.executeUpdate();
        //5. Release resources
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtil.close(null, ps, conn);
    }

}

//Delete student information
public void delete(Long id) {
    String sql = "DELETE  FROM t_student WHERE id=?";
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        conn=JDBCUtil.getConnection();
        //3. Create statement object
        ps = conn.prepareStatement(sql);
        ps.setObject(1, id);
        //4. Execute SQL statement
        ps.executeUpdate();
        //5. Release resources
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtil.close(null, ps, conn);
    }

}

//Modify student information
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. Create statement object
        ps = conn.prepareStatement(sql);
        ps.setObject(1, stu.getName());
        ps.setObject(2, stu.getAge());
        ps.setObject(3, stu.getId());
        //4. Execute SQL statement
        ps.executeUpdate();
        //5. Release resources
    } 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. Create statement object
        ps = conn.prepareStatement(sql);
        ps.setObject(1, id);
        //4. Execute SQL statement
        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. Release resources
    } 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. Create statement object
        ps = conn.prepareStatement(sql);
        //4. Execute SQL statement
        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. Release resources
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtil.close(rs, ps, conn);
    }
    return list;
}

Although the extraction of duplicate codes is completed, the account and password in the database are directly displayed in the code, which is not conducive to the maintenance of later account password changes. You can create a DB. Property file to store this information.

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

Just get the information in the tool class JdbcUtil.

static {
    //1. Load the registered driver
    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. Get database connection
        return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"),
        p.getProperty("password"));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

It seems that the code extraction has been completed here, but there are still some duplicate codes in the implementation class. In DML operation, except for the differences between SQL and setting values, the other parts are the same. Extract the same parts and pass in different parts through parameters, which cannot be directly placed in the tool class. At this point, you can create a template class JdbcTemplate and create a DML and DQL template to refactor the code.

//Query unified template the first step is to find
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);
        //Set value
        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. Release resources
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtil.close(rs, ps, conn);
    }
    return list;
}

The implementation class can call the method directly.
//Add student information
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);
}

//Delete student information
public void delete(Long id) {
    String sql = "DELETE FROM t_student WHERE id = ?";
    JdbcTemplate.update(sql, id);
}

//Modify student information
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);
}

In this way, the repeated code is basically solved, but there is a serious problem that the DQL operation of this program can only deal with Student class and t_ The data related to the Student table cannot be processed by other classes, such as teacher class and t_teacher table. Different tables (different objects) should have different columns, and the codes for processing result sets in different columns should be different. Only the DAO itself knows the operation of processing result sets best. In other words, the method of processing results should not be placed in the template method at all, but should be handled by each DAO itself. Therefore, you can create an IRowMapper interface to process the result set.

public interface IRowMapper {
    //Processing result set
    List rowMapper(ResultSet rs) throws Exception;
}

The DQL template class calls the handle method in the IRowMapper interface to remind the implementation class to implement the mapping method by itself.

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);
        //Set value
        for (int i = 0; i < params.length; i++) {
            ps.setObject(i+1, params[i]);
        }
        rs = ps.executeQuery();
        return rsh.mapping(rs);
        //5. Release resources
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(rs, ps, conn);
    }
    return list ;
}

The implementation class implements the mapping method of the IRowMapper interface. You can define what type of data you want to process in it.

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;
    }
}

So far, the key code to implement ORM has been completed, but DQL query not only needs to query student information (List type), but also the number of students. At this time, it needs to be completed through generics.

public interface IRowMapper<T> {
    //Processing result set
    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);
        //Set value
        for (int i = 0; i < params.length; i++) {
            ps.setObject(i+1, params[i]);
        }
        rs = ps.executeQuery();
        return rsh.mapping(rs);
        //5. Release resources
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(rs, ps, conn);
    }
    return null;
}

The code of the StudentRowMapper class is as follows.

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;
    }
}

In this way, you can query not only the List, but also the number of students.

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;
}

In this way, the refactoring design has been completed. Good code can make it easier for us to maintain in the future. Therefore, it is very important to learn to refactor the code.

3 classic frameworks are using design patterns to solve problems

For example, Spring is a classic framework that uses design patterns incisively and vividly. This book will analyze design patterns in combination with the source code of classic frameworks such as JDK, Spring, MyBatis, Netty, Tomcat and Dubbo, so as to help you better and more deeply understand the landing of design patterns in the framework source code.

[recommendation] Tom bomb architecture: collecting this article is equivalent to collecting a book on "design patterns"
This article is the original of "Tom bomb architecture". Please indicate the source for reprint. Technology lies in sharing, I share my happiness!
If this article is helpful to you, you are welcome to pay attention and praise; If you have any suggestions, you can also leave comments or private letters. Your support is the driving force for me to adhere to my creation. Focus on WeChat official account Tom structure, get more dry cargo!

Tags: Java Back-end architecture

Posted on Fri, 29 Oct 2021 11:20:18 -0400 by Michdd