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!