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);
		}
好了,重构设计已经完成,好的代码能让我们以后维护更方便,因此学会对代码的重构是很重要的






  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * 原始需求背景: * 网宿CDN要按月收取客户的服务费用,根据流量的大小、 * 服务的类型等,收取不同的费用,收费规则如下: * web应用:1000元/M * 流媒体应用:1000元/M*0.7 * 下载应用:1000元/M*0.5 * 月末打印报表时,要罗列每个用户每个频道的费用、客户总费用, * 还要打印该客户的重要性指数,重要性指数=网页流/100+下载流量/600; * * 需求变更场景: * 系统已经开发出来了,接下来,运维部门现在希望对系统做一点修改, * 首先,他们希望能够输出xml,这样可以被其它系统读取和处理,但是, * 这段代码根本不可能在输出xml的代码中复用report()的任何行为,唯一 * 可以做的就是重写一个xmlReport(),大量重复report()中的行为,当然, * 现在这个修改还不费劲,拷贝一份report()直接修改就是了。 * 不久,成本中心又要求修改计费规则,于是我们必须同时修改xmlReport() * 和report(),并确保其一致性,当后续还要修改的时候,复制-黏贴的问题就 * 浮现出来了,这造成了潜在的威胁。 * 再后来,客服部门希望修改服务类型和用户重要性指数的计算规则, * 但还没决定怎么改,他们设想了几种方案,这些方案会影响用户的计费规则, * 程序必须再次同时修改xmlReport()和report(),随着各种规则变得越来越复杂, * 适当的修改点越 来越难找,不犯错误的机会越来越少。 * 现在,我们运用所学的OO原则和方法开始进行改写吧。 */

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值