共计 12109 个字符,预计需要花费 31 分钟才能阅读完成。
先来看一个生存案例,当咱们开心时,兴许会寻求吃苦。在学习设计模式之前,你可能会这样感叹:
学完设计模式之后,你可能会这样感叹:
大家比照一下前后的区别,有何感触?
回到代码中,咱们来思考一下,设计模式能解决哪些问题?
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.Driver
url=jdbc:mysql:///jdbcdemo
username=root
password=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 弹架构』可获取更多技术干货!