本篇博客参考《信息领域热词分析》,设计实现可测试性战术。
首先我们要了解一下可测试性,软件可测试性是指通过测试(通常是基于运行的测试)揭示软件缺陷的容易程度。
接下来就介绍在项目开发中运用的可测试性战术:
1.面向对象编程
作为一名软件工程的学生,我们都知道编码原则:
- 单一责任原则
- 开放/封闭原则
- 里氏代换原则
- 接口分离原则
- 依赖反转原则
不论是在爬虫爬取信息阶段,还是热词统计阶段,还有最后的可视化展示,都离不开这些原则。尤其是遵循了单一职责原则和接口分离原则后,面向接口编程,
每一个函数或者对象,都只完成单一的功能,主函数只对其进行调用或传参,可测试性就会大大提高,这些原则会引导我们持续重构。
单一职责:
interface UserOpr2 { boolean updatePassword(User user, String password); boolean updateUserInfo(User user); } class UserOprImpl2 implements UserOpr2 { @Override public boolean updatePassword(User user, String password) { user.setPassword(password); // update password return true; } @Override public boolean updateUserInfo(User user) { // update user info return true; } }
修改密码和修改名字分离开来,也就是把修改密码和修改名字都当做独自的职责处理,这样子就很清晰明了,你调用哪个方法,就很明确的知道这个方法是实现什么逻辑。
接口隔离(提高了系统的内聚性,减少了对外交互,降低了系统的耦合性):
package org.byron4j.cookbook.designpattern.segregation; /** * 施乐公司系统机器接口 */ public interface IMachine { /** * 打印 */ public void print(); /** * 装订 */ public void staple(); /** * 扫描 */ public void scan(); /** * 复印 */ public void photoCopy(); } package org.byron4j.cookbook.designpattern.segregation; public class XeroxMachine implements IMachine { @Override public void print() { System.out.println("打印任务..."); } @Override public void staple() { System.out.println("装订任务..."); } @Override public void scan() { System.out.println("扫描任务..."); } @Override public void photoCopy() { System.out.println("复印任务..."); } }
2.使用设计模式
上学期我们学习过设计模式这门课程,设计模式其实是代码经验的总结。
比如策略模式,策略模式定义了一组算法,将每个算法都封装起来,并使它们之间可以互换。这个模式让法算法的变化独立于客户端的调用。
策略模式优点:1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
package com.hotwords.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import com.hotwords.entity.entity; public class dao { public List<entity> list1(){ List<entity> list =new ArrayList<entity>(); try { // 加载数据库驱动,注册到驱动管理器 Class.forName("com.mysql.jdbc.Driver"); // 数据库连接字符串 String url = "jdbc:mysql://localhost:3306/xinwen?useUnicode=true&characterEncoding=utf-8"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "893225523"; // 创建Connection连接 Connection conn = DriverManager.getConnection(url, username, password); // 添加图书信息的SQL语句 String sql = "select * from final_hotword"; // 获取Statement Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { entity book = new entity(); book.setHotwords(resultSet.getString("热词")); book.setNum(resultSet.getString("次数")); list.add(book); } resultSet.close(); statement.close(); conn.close(); }catch (Exception e) { e.printStackTrace(); } return list; } // public List<entity> list2(){ List<entity> list =new ArrayList<entity>(); try { // 加载数据库驱动,注册到驱动管理器 Class.forName("com.mysql.jdbc.Driver"); // 数据库连接字符串 String url = "jdbc:mysql://localhost:3306/xinwen?useUnicode=true&characterEncoding=utf-8"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "893225523"; // 创建Connection连接 Connection conn = DriverManager.getConnection(url, username, password); // 添加图书信息的SQL语句 String sql = "select * from website"; // 获取Statement Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { entity book = new entity(); book.setHotwords(resultSet.getString("热词")); book.setExplain(resultSet.getString("解释")); book.setWebsite(resultSet.getString("网址")); list.add(book); } resultSet.close(); statement.close(); conn.close(); }catch (Exception e) { e.printStackTrace(); } return list; } }
3.特定化访问接口
面向接口编程有两层含义:类级别,面向接口编程; 方法级别,面向函数接口编程。
既定的接口具有自我描述性,并能够促进代码的重用性,接口可以提供一种信息,告诉外部一个类需要实现哪些方法。还有助于稳定不同类之间的通信方式,减少了继承两个对象的过程中出现的问题。
比如 A 对象依赖 B 对象里的 M 方法,而 M 方法会从数据库里读取数据。那么 A 就不要直接依赖 B 的实体类,而引用 B 的接口。 当对 A 编写单测时,只要注入 B 的 接口实现即可。 同理,方法中含有 service 调用时,不要直接依赖 service 调用,而是依赖函数接口,在函数接口中传递 service 调用,如上面的做法。
@FunctionalInterface public interface Predicate<T>{ boolean test(T t); } public static <T> List<T> filter(List<T> list, Predicate<T> p) { List<T> results = new ArrayList<>(); for(T s: list){ if(p.test(s)){ results.add(s); } } return results; } Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty(); List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);