1 回顾软件设计原则
文章推荐IDEA快速构建UML类图:https://blog.csdn.net/ZGL_cyy/article/details/118278233
不用设计模式并非不可以,但是用好设计模式能帮助我们更好地解决实际问题,设计模式最重要的是解耦。设计模式天天都在用,但自己却无感知。我们把设计模式作为一个专题,主要是学习设计模式是如何总结经验的,把经验为自己所用。学设计模式也是锻炼将业务需求转换技术实现的一种非常有效的方式。
设计原则 | 解释 |
开闭原则 | 对扩展开放,对修改关闭 |
依赖倒置原则 | 通过抽象使各个类或者模块不相互影响,实现松耦合。 |
单一职责原则 | 一个类、接口、方法只做一件事。 |
接口隔离原则 | 尽量保证接口的纯洁性,客户端不应该依赖不需要的接口。 |
迪米特法则 | 又叫最少知道原则,一个类对其所依赖的类知道得越少越好。 |
里氏替换原则 | 子类可以扩展父类的功能但不能改变父类原有的功能。 |
合成复用原则 | 尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的。 |
2 设计模式总览
写出优雅的代码以下是超级乱的代码
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")); ... }
优雅的赋值:
注:这里的下划线可以转换一下驼峰这里我要总结几篇文章
@JSONType(naming= PropertyNamingStrategy.SnakeCase)
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(); } }
更好地重构项目
请查看以下代码发现问题
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等经典框架的源码对设计模式展开分析,帮助大家更好、更深入地理解设计模式在框架源码中的落地。
经典框架都在用设计模式解决问题
Spring就是一个把设计模式用得淋漓尽致的经典框架,其实从类的命名就能看出来,我来一一列举:
设计模式名称 | 举例 |
工厂模式 | BeanFactory |
装饰器模式 | BeanWrapper |
代理模式 | AopProxy |
委派模式 | DispatcherServlet |
策略模式 | HandlerMapping |
适配器模式 | HandlerAdapter |
模板模式 | JdbcTemplate |
观察者模式 | ContextLoaderListener |
围绕 Spring 的 IOC、AOP、MVC、JDBC这样的思路展开,根据其设计类型来设计讲解顺序:
类型 | 名称 | 英文 |
创建型模式 | 工厂模式 | Factory Pattern |
单例模式 |
Singleton Pattern | |
原型模式 | Prototype Pattern | |
结构型模式 | 适配器模式 | Adapter Pattern |
装饰器模式 | Decorator Patter | |
代理模式 | Proxy Pattern |
行为性模式 | 策略模式 | Strategy Pattern |
模板模式 | Template Pattern | |
委派模式 | Delegate Pattern | |
观察者模式 | Observer Pattern |