质量属性II(信息领域热词分析)
可修改性描述了程序能够被正确修改的难易程度。在实际项目中,我们会经常遇到这样一个问题,用户修改需求,这样对于我们编码人员来说势必需要修改我们已经完成的代码或者重新设计架构,这时我们就需要考虑一个问题:我们的代码能不能修改,修改了某一部分系统其它部分是否会受到影响。软件工程中经常会提到高内聚低耦合六个核心思想,其主要目的就是使程序模块的可重用性、移植性大大增强。
一,局部化修改
1.维持语义一致性(抽象通用服务)
思维发散:相信我们大家都了解经典的TCP/IP协议,互联网分层架构,其核心思想是:
-
让上游更高效的获取与处理数据,复用
-
让下游能屏蔽数据的获取细节,封装
类比到我们软件开发中来,我们也有Java Web经典三层架构,表示层(web层):Servlet,Jsp;业务逻辑层(Service层);数据访问层(dao层):CRUD。
从而,我们可以封装一个数据库连接类DBUtils.java,当我们的项目需要更改数据库时,只需要修改该类即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.wang.util; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 public class DBUtil { 10 public static String db_url = "jdbc:mysql://127.0.0.1:3306/wang?useSSL=false&characterEncoding=utf8"; 11 public static String db_user = "root"; 12 public static String db_pass = "123456"; 13 14 public static Connection getConn () { 15 Connection conn = null; 16 17 try { 18 Class.forName("com.mysql.jdbc.Driver");//加载驱动 19 conn = DriverManager.getConnection(db_url, db_user, db_pass); 20 } catch (Exception e) { 21 e.printStackTrace(); 22 } 23 24 return conn; 25 } 26 27 /** 28 * 关闭连接 29 * @param state 30 * @param conn 31 */ 32 public static void close (Statement state, Connection conn) { 33 if (state != null) { 34 try { 35 state.close(); 36 } catch (SQLException e) { 37 e.printStackTrace(); 38 } 39 } 40 41 if (conn != null) { 42 try { 43 conn.close(); 44 } catch (SQLException e) { 45 e.printStackTrace(); 46 } 47 } 48 } 49 50 public static void close (ResultSet rs, Statement state, Connection conn) { 51 if (rs != null) { 52 try { 53 rs.close(); 54 } catch (SQLException e) { 55 e.printStackTrace(); 56 } 57 } 58 59 if (state != null) { 60 try { 61 state.close(); 62 } catch (SQLException e) { 63 e.printStackTrace(); 64 } 65 } 66 67 if (conn != null) { 68 try { 69 conn.close(); 70 } catch (SQLException e) { 71 e.printStackTrace(); 72 } 73 } 74 } 75 76 }
当我们需要对数据库中某张表进行CRUD操作时也可以封装一个类Dao.java并在其中创建数据库连接类DBUtils.java的对象连接数据库。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.wang.dao; 2 3 import java.sql.Connection; 4 import java.sql.ResultSet; 5 import java.sql.Statement; 6 7 import com.wang.util.DBUtil; 8 9 public class Dao { 10 11 public static String getConfirmByCity(String city) { 12 String sql = "select confirm from all_data where city ='" + city + "'"; 13 Connection conn = DBUtil.getConn(); 14 Statement state = null; 15 ResultSet rs = null; 16 String confirm=null; 17 try { 18 state = conn.createStatement(); 19 rs = state.executeQuery(sql); 20 while (rs.next()) { 21 confirm = rs.getString("confirm"); 22 } 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } finally { 26 DBUtil.close(rs, state, conn); 27 } 28 return confirm; 29 } 30 31 }
以上两个类之间相互独立,任意修改其中一个类,另一个类可以正常工作,两者通过一个对象进行数据交流。数据库连接类DBUtils.java就好比一项服务,可以被反复重用,当出现问题时,修改该服务即可,被服务的“应用”不受影响。
2.预期期望的变更
预期期望变更的战术并不关心模块责任的一致性,他所关心的是使变更的影响最小。在实际中很难单独使用该战术,因为不可能预期所有变更。基于此原因,我们通常结合语义一致性来使用该战术。
3.泛化模块
泛化模块,即使模块更加通用,而非修改它。
我们还用连接数据库举例子,假如开发一个项目,以MySql作为后台数据库,连接类如下:
1 public class DBUtil { 2 3 public static Connection getMySqlConn () { 4 public static String db_url = "jdbc:mysql://127.0.0.1:3306/wang?useSSL=false&characterEncoding=utf8"; 5 public static String db_user = "root"; 6 public static String db_pass = "123456"; 7 Connection conn = null; 8 9 try { 10 Class.forName("com.mysql.jdbc.Driver");//加载驱动 11 conn = DriverManager.getConnection(db_url, db_user, db_pass); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } 15 16 return conn; 17 } 18 }
当项目开发到某个阶段时,由于某种原因,需要将后台数据库更换为SQL Sever数据库时,我们并不一定要修改getMySqlConn()函数,最为合理的方法是添加一个新函数getSqlServerConn(),调用该函数即可。新的数据库连接类如下:
1 public class DBUtil { 2 3 public static Connection getMySqlConn () { 4 public static String db_url = "jdbc:mysql://127.0.0.1:3306/wang?useSSL=false&characterEncoding=utf8"; 5 public static String db_user = "root"; 6 public static String db_pass = "123456"; 7 Connection conn = null; 8 try { 9 Class.forName("com.mysql.jdbc.Driver");//加载驱动 10 conn = DriverManager.getConnection(db_url, db_user, db_pass); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } 14 return conn; 15 } 16 17 public static Connection getSqlServerConn () { 18 public static String db_url = "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=student_db;"; 19 public static String db_user = "sa"; 20 public static String db_pass = "admin"; 21 Connection conn = null; 22 try { 23 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");//加载驱动 24 conn = DriverManager.getConnection(db_url, db_user, db_pass); 25 } catch (Exception e) { 26 e.printStackTrace(); 27 } 28 return conn; 29 } 30 }
4.限制可能的选择
我们都知道浏览器(IE各版本,Chrome,Firefox)对于网页的显示效果有一定的差异,所以我们可以限制用户使用某一款浏览器才能达到最好的视觉效果。比如我们现在使用的超星平台建议使用IE8以上浏览器,谷歌浏览器,火狐浏览器。假如使用IE7,就有可能无法正常显示。
二,防止连锁反应
1.信息隐藏
将实体类中的变量私有化,只能通过公开的get,set方法进行访问。
1 public class HotCi { 2 private String word; 3 public String getWord() { 4 return word; 5 } 6 public void setWord(String word) { 7 this.word = word; 8 } 9 }
2.维持现有的接口
假定B类依赖A类中的接口Show,如下:
1 public class A { 2 3 void cry() { 4 System.out.println("被适配者的方法"); 5 } 6 7 interface Show { 8 void display(); 9 } 10 }
1 import A.Show; 2 public class B implements Show { 3 4 @Override 5 public void display() { 6 System.out.println("我依赖A中的接口Show"); 7 } 8 9 }
(1)增加接口
需求:提供访问smile()函数接口;
分析:不能需改A中现有接口,因为有B依赖,一旦修改B就会出问题,所以应该添加接口:
1 interface Show_s { 2 void smile(); 3 }
(2)添加适配器
需求:目标接口Target调用A中的cry()函数
分析:通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
1 public interface Target { 2 void request(); 3 }
1 public class Adapter extends A implements Target{ 2 @Override 3 public void request() { 4 //...一些操作... 5 super.cry(); 6 //...一些操作... 7 } 8 }
(3)占位程序
如果修改要求删除A,且B仅依赖于A的签名,那么为A提供一个占位程序能够使B保持不变。
3.限制通信路径
限定一个给定的模块用来共享数据,包括生产和使用该模块的数据。JavaBean模块用来共享数据:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private void add(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { 2 req.setCharacterEncoding("utf-8"); 3 String name = req.getParameter("name"); 4 String money = req.getParameter("money"); 5 String date = req.getParameter("date"); 6 Student course = new Student(name,money,date); 7 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 try { 2 state = conn.createStatement(); 3 rs = state.executeQuery(sql); 4 Student bean = null; 5 while (rs.next()) { 6 int id = rs.getInt("id"); 7 String name = rs.getString("name"); 8 String money = rs.getString("money"); 9 String date = rs.getString("date"); 10 bean = new Student(id,name,money,date); 11 list.add(bean); 12 } 13 }
4.使用仲裁者
资源管理器作为仲裁者进行资源的分配。调度A与B之间的资源使用情况防止死锁。
三,推迟绑定时间
1.运行时注册
2.配置文件
我每次启动eclipse时,都会弹出选择工作空间,及设置存储参数。
3.组件更换
4.遵守已定义的协议