疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java代码重构详解


 

 
平时我们写的代码虽然满足了需求但往往不利于项目的开发与维护,以下面的JDBC代码为例
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
<span style="font-size:18px;">// 增加学生信息  
    @Override  
    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:///jdbcdemo", "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();  
                }  
            }  
  
        }  
  
    }  
  
    // 删除学生信息  
    @Override  
    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:///jdbcdemo", "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();  
                }  
            }  
  
        }  
  
    }  
  
    // 修改学生信息  
    @Override  
    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:///jdbcdemo", "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();  
                }  
            }  
  
        }</span>  
上述代码中功能没问题,但是代码重复的太多,因此我们可以进行抽取,把重复的代码放到一个工具类JDBCUtil2里
 
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//工具类  
public class JDBCUtil2 {  
    private JDBCUtil2() {  
    }  
  
    static {  
        // 1. 加载注册驱动  
        try {             
            Class.forName("com.mysql.jdbc.Driver");  
        } catch (Exception e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
  
    public static Connection getConnection() {  
        try {  
            // 2. 获取数据库连接  
            return DriverManager.getConnection("jdbc:mysql:///jdbcdemo", "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();  
                }  
            }  
        }  
    }  
  
}  
在实现类中直接调用工具类中的方法即可
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public class StudentDAOImpl2 implements IstudentDAO {  
  
    // 增加学生信息  
    @Override  
    public void save(Student stu) {  
        String sql = "INSERT INTO t_student(name,age) VALUES(?,?)";  
        Connection conn = null;  
        PreparedStatement ps=null;  
        try {  
            conn=JDBCUtil2.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 {  
            JDBCUtil2.close(null, ps, conn);  
        }  
  
    }  
  
    // 删除学生信息  
    @Override  
    public void delete(Long id) {  
        String sql = "DELETE  FROM t_student WHERE id=?";  
        Connection conn = null;  
        PreparedStatement ps=null;  
        try {  
            conn=JDBCUtil2.getConnection();  
            // 3. 创建语句对象  
             ps = conn.prepareStatement(sql);  
            ps.setObject(1, id);  
            // 4. 执行SQL语句  
            ps.executeUpdate();  
            // 5. 释放资源  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            JDBCUtil2.close(null, ps, conn);  
        }  
  
    }  
  
    // 修改学生信息  
    @Override  
    public void update(Student stu) {  
        String sql = "UPDATE t_student SET name=?,age=? WHERE id=?";  
        Connection conn = null;  
        PreparedStatement ps=null;  
        try {  
            conn=JDBCUtil2.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 {  
            JDBCUtil2.close(null, ps, conn);  
        }  
  
    }  
  
    @Override  
    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=JDBCUtil2.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 {  
            JDBCUtil2.close(rs, ps, conn);  
        }  
        return null;  
    }  
  
    @Override  
    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=JDBCUtil2.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 {  
            JDBCUtil2.close(rs, ps, conn);  
        }  
        return list;  
    }  
}  
虽然完成了重复代码的抽取,但数据库中的账号密码等直接显示在代码中,不利于后期账户密码改动的维护,我们可以建立个db.propertise文件用来存储这些信息
 
[html] view plain copy 在CODE上查看代码片派生到我的代码片
driverClassName =com.mysql.jdbc.Driver  
url =jdbc:mysql:///jdbcdemo  
username =root  
password =root  
只需在工具类中获取里面的信息即可
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
private static Properties p;  
    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) {  
            // TODO Auto-generated catch block  
            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的模板来进行对代码的重构。
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public class JDBCTemplate {  
    private JDBCTemplate(){};  
    //DML通用模板  
    public static void update(String sql, Object... params) {  
        Connection conn = null;  
        PreparedStatement ps = null;  
        try {  
            conn = JDBCUtil2.getConnection();  
            ps = conn.prepareStatement(sql);  
            // 设置值  
            for (int i = 0; i < params.length; i++) {  
                ps.setObject(i + 1, params[i]);  
            }  
            ps.executeUpdate();  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            JDBCUtil.close(null, ps, conn);  
        }  
    }  
    //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;  
    }  
}  
实现类直接调用方法即可。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
// 增加学生信息  
    @Override  
    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);  
    }  
  
    // 删除学生信息  
    @Override  
    public void delete(Long id) {  
        String sql = "DELETE  FROM t_student WHERE id=?";  
        JDBCTemplate.update(sql, id);  
    }  
  
    // 修改学生信息  
    @Override  
    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);  
    }  
  
    @Override  
    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;  
    }  
  
    @Override  
    public List<Student> list() {  
        String sql = "SELECT * FROM t_student ";  
        return JDBCTemplate.query(sql);  
    }  
这样重复的代码基本就解决了,但又个很严重的问题就是这个程序DQL操作中只能处理Student类和t_student表的相关数据,无法处理其他类如:Teacher类和t_teacher表。不同表(不同的对象),不同的表就应该有不同列,不同列处理结果集的代码就应该不一样,处理结果集的操作只有DAO自己最清楚,也就是说,处理结果的方法压根就不应该放在模板方中,应该由每个DAO自己来处理。因此我们可以创建一个IResultSetHandle接口来处理结果集
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public interface IResultSetHandle {  
    //处理结果集  
    List handle(ResultSet rs) throws Exception;  
}  
DQL模板类中调用IResultSetHandle接口中的handle方法,提醒实现类去自己去实现handle方法
 
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//DQL同意模板  
    public static List<Student> query(String sql,<span style="color:#ff0000;">IResultSetHandle rsh</span>, 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();  
            <span style="color:#ff0000;">return rsh.handle(rs);</span>  
            // 5. 释放资源  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            JDBCUtil.close(rs, ps, conn);  
        }  
        return list;  
    }  
 
 
实现类自己去实现IResultSetHandle接口的handle方法,想要处理什么类型数据在里面定义即可
[java] view plain copy 在CODE上查看代码片派生到我的代码片
@Override  
    public Student get(Long id) {  
        String sql = "SELECT * FROM t_student WHERE id=?";  
        List<Student> list = JDBCTemplate.query(sql,new StudentResultSetHandle(), id);  
        return list.size()>0? list.get(0):null;  
    }  
  
    @Override  
    public List<Student> list() {  
        String sql = "SELECT * FROM t_student ";  
        return JDBCTemplate.query(sql,new StudentResultSetHandle());  
    }  
      
    class StudentResultSetHandle implements IResultSetHandle{  
  
        @Override  
        public List handle(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;  
        }  
          
    }   
 
好了,基本已经大功告成了,但是DQL查询不单单只有查询学生信息(List类型),还可以查询学生数量,这时就要通过泛型来完成
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//声明泛型T作为返回类型,谁调用IResultSetHandle,谁就决定T类型  
public interface IResultSetHandle<T> {  
    //处理结果集  
    T handle(ResultSet rs) throws Exception;  
}  
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//DQL同意模板  
    public static <T> T query(String sql,IResultSetHandle<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.handle(rs);  
            // 5. 释放资源  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            JDBCUtil.close(rs, ps, conn);  
        }  
        return null;  
    }  
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
class StudentResultSetHandle implements IResultSetHandle<List<Student>>{  
  
        public List<Student> handle(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,还可以查询学生数量
 
[java] view plain copy 在CODE上查看代码片派生到我的代码片
// 查询学生整数  
        @Test  
        public void testgetTotal() throws Exception {  
            Long totalCount = JDBCTemplate.query("SELECT COUNT(*) tatal FROM t_student", new IResultSetHandle<Long>() {  
  
                @Override  
                public Long handle(ResultSet rs) throws Exception {  
                    Long totalCount=null;  
                    if(rs.next()){  
                        totalCount=rs.getLong("tatal");  
                    }  
                    return totalCount;  
                }  
            });  
            System.out.println(totalCount);  
        }  
好了,重构设计已经完成,好的代码能让我们以后维护更方便,因此学会对代码的重构是很重要的