java代码重构
Posted yuzipei1132
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java代码重构相关的知识,希望对你有一定的参考价值。
平时我们写的代码虽然满足了需求但往往不利于项目的开发与维护,以下面的JDBC代码为例
<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里
//工具类 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(); } } } } }在实现类中直接调用工具类中的方法即可
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文件用来存储这些信息
driverClassName =com.mysql.jdbc.Driver url =jdbc:mysql:///jdbcdemo username =root password =root只需在工具类中获取里面的信息即可
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的模板来进行对代码的重构。
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; } }实现类直接调用方法即可。
// 增加学生信息 @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接口来处理结果集
public interface IResultSetHandle { //处理结果集 List handle(ResultSet rs) throws Exception; }
DQL模板类中调用IResultSetHandle接口中的handle方法,提醒实现类去自己去实现handle方法
//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方法,想要处理什么类型数据在里面定义即可
@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类型),还可以查询学生数量,这时就要通过泛型来完成
//声明泛型T作为返回类型,谁调用IResultSetHandle,谁就决定T类型 public interface IResultSetHandle<T> { //处理结果集 T handle(ResultSet rs) throws Exception; }
//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; }
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,还可以查询学生数量
// 查询学生整数 @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); }好了,重构设计已经完成,好的代码能让我们以后维护更方便,因此学会对代码的重构是很重要的
以上是关于java代码重构的主要内容,如果未能解决你的问题,请参考以下文章