先来看一个生存案例,当咱们开心时,兴许会寻求吃苦。在学习设计模式之前,你可能会这样感叹:

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

大家比照一下前后的区别,有何感触?

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

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.Driverurl=jdbc:mysql:///jdbcdemousername=rootpassword=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弹架构 』可获取更多技术干货!