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代码重构的主要内容,如果未能解决你的问题,请参考以下文章

java代码重构

java代码重构

java代码重构

一篇讲透Java重构之道

精练代码:一次Java函数式编程的重构之旅

Java精练代码:一次Java函数式编程的重构之旅