首先对可修改性战术进行分析。
可修改性战术的目标是控制实现、测试和部署变更的时间和成本。
一组修改性战术的目标是减少由某个变更直接影响的模块的数量。我们把这组可修改性战术称为“局部化修改”;
维持语义的一致性。语义的一致性指的是模块中责任之间的关系。目标是确保所有这些责任都能够协同工作,不需要过多地依赖其他模块。该目标是通过选择具有语义一致性的责任来实现的。耦合和内聚指标是度量语义一致性的尝试,但它们遗漏了变更的上下文。相反,应该根据一组预期的变更来度量语义一致性。其中的一个子战术就是“抽象通用服务”。通过专门的模块提供通用服务通常被视作支持重用。这是正确的,但抽象通用服务也支持可修改性。如果己经抽象出了通用服务,那么,对这些通用服务的修改只需要进行一次,而不需要在使用这些服务的每个模块中都进行修改。此外,对使用这些服务的模块的修改不会影响其他的用户。因此,该战术不仅支持局部化修改,而且还能够防止连锁反应。抽象通用服务的示例就是应用框架的使用和其他中间件软件的使用。
下面从数据库连接代码编写的层面对这一战术进行实践。
DBUtil.java
package hotWords.util;
import java.sql.*;
public class DBUtil {
public static Connection getConnection() throws ClassNotFoundException, SQLException {
String JDBC_DRIVER = "com.mysql.jdbc.Driver";
String DB_URL = "jdbc:mysql://localhost:3306/hotwords?useUnicode=true&characterEncoding=UTF-8&useSSL=false";
String USER = "root";
String PASS = "";
@SuppressWarnings("unused")
Connection conn = null;
Class.forName(JDBC_DRIVER);
System.out.println("连接数据库...");
return conn = DriverManager.getConnection(DB_URL,USER,PASS);
}
public static void close(Connection connection ) {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void close(PreparedStatement preparedStatement ) {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void close(ResultSet resultSet ) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
将数据库连接的操作这一通用服务存放到一个文件里面,其他模块可以直接调用,修改时(修改数据库连接用户名密码等)只需修改这一个文件,其余内容不用修改。
泛化该模块。使一个模块更通用能够使它根据输入计算更广泛的功能。可以把该输入看作是为该模块定义了一种语言,这可能会如同使常数成为输入参数一样简单;也可能会如同把该模块实现为解释程序,并使输入参数成为解释程序的语言中的程序一样复杂。模块越通用,越有可能通过调整语言而非修改模块来进行请求的变更。
LoadDaoImpl.java
package hotWords.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import hotWords.bean.Explain;
import hotWords.bean.News;
import hotWords.bean.Relation;
import hotWords.bean.Title;
import hotWords.util.DBUtil;
public class LoadDaoImpl {
public List<Explain> loadhot() throws ClassNotFoundException, SQLException {
Connection connection=DBUtil.getConnection();
String sql="select * from keywords";
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
Explain explain=null;
List<Explain> explains=new ArrayList<>();
int flag=1;
try {
preparedStatement=connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
if(resultSet.getInt("num")<300)
continue;
explain=new Explain();
explain.setWords(resultSet.getString("word"));
explain.setNum(resultSet.getInt("num"));
explain.setExp(resultSet.getString("exp"));
explain.setId(flag);
explains.add(explain);
flag++;
}
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
DBUtil.close(resultSet);
DBUtil.close(preparedStatement);
DBUtil.close(connection);
}
return explains;
}
public List<Explain> load() throws ClassNotFoundException, SQLException {
Connection connection=DBUtil.getConnection();
String sql="select * from keywords";
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
Explain explain=null;
List<Explain> explains=new ArrayList<>();
int flag=1;
try {
preparedStatement=connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
explain=new Explain();
explain.setWords(resultSet.getString("word"));
explain.setNum(resultSet.getInt("num"));
explain.setExp(resultSet.getString("exp"));
explain.setId(flag);
explains.add(explain);
flag++;
}
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
DBUtil.close(resultSet);
DBUtil.close(preparedStatement);
DBUtil.close(connection);
}
return explains;
}
public Explain loadmore(String wordname) throws ClassNotFoundException, SQLException {
Connection connection=DBUtil.getConnection();
String sql="select * from keywords where word = "+"'"+wordname+"'";
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
Explain explain=null;
try {
preparedStatement=connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
explain=new Explain();
explain.setWords(resultSet.getString("word"));
explain.setExp(resultSet.getString("exp"));
}
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
DBUtil.close(resultSet);
DBUtil.close(preparedStatement);
DBUtil.close(connection);
}
return explain;
}
@SuppressWarnings("null")
public List<News> loadnews(String wordname) throws ClassNotFoundException, SQLException {
Connection connection=DBUtil.getConnection();
String sql="select * from words where word = "+"'"+wordname+"'";
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
List<News> news=new ArrayList<>();
News n=null;
try {
preparedStatement=connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
n=new News();
n.setTitle(resultSet.getString("title"));
n.setLink(resultSet.getString("link"));
news.add(n);
}
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
DBUtil.close(resultSet);
DBUtil.close(preparedStatement);
DBUtil.close(connection);
}
return news;
}
public List<Relation> loadrelation(Title title) throws ClassNotFoundException, SQLException {
Connection connection=DBUtil.getConnection();
String sql="select * from words where title = "+"'"+title.getTitle()+"'";
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
List<Relation> relations=new ArrayList<>();
Relation relation=null;
try {
preparedStatement=connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
relation=new Relation();
relation.setTitle(resultSet.getString("title"));
relation.setWord(resultSet.getString("word"));
relations.add(relation);
}
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
DBUtil.close(resultSet);
DBUtil.close(preparedStatement);
DBUtil.close(connection);
}
return relations;
}
public List<Title> loadtitle() throws ClassNotFoundException, SQLException {
Connection connection=DBUtil.getConnection();
String sql="select * from tit";
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
List<Title> titles=new ArrayList<>();
Title title=null;
try {
preparedStatement=connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
if(Integer.parseInt(resultSet.getString("num"))<14)
continue;
title=new Title();
title.setTitle(resultSet.getString("title"));
titles.add(title);
}
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
DBUtil.close(resultSet);
DBUtil.close(preparedStatement);
DBUtil.close(connection);
}
return titles;
}
}
关于数据加载并且处理后存放的方法都在这一个文件里面,只要导入这个文件,热词系统中所有关于数据调用的方法都可以使用。
防止连锁反应
修改所产生的一个连锁反应就是需要改变该修改并没有直接影响到的模块。例如,如果改变了模块A以完成某个特定的修改,那么必须改变模块B,这仅仅是因为改变了模块A。必须修改模块B,在某种意义上来说,是因为它依赖于模块A。
信息隐藏
信息隐藏就是把某个实体(一个系统或系统的某个分解)的责任分解为更小的部分,并选择使哪些信息成为公有的,哪些信息成为私有的。可以通过指定的接口获得公有责任。信息隐藏的目的是将变更隔离在一个模块内,防止变更扩散到其他模块。这是防止变更扩散的最早的技术。它与“预期期望的变更有很大关系”,因为它使用那些变更作为分解的基础。
PrintWord.java
package hotWords.dao;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.sql.SQLException;
import hotWords.bean.Explain;
public class PrintWord {
public void word() throws ClassNotFoundException, SQLException {
LoadDaoImpl loadDaoImpl=new LoadDaoImpl();
List<Explain> explains=null;
explains=loadDaoImpl.load();
String title = "热词解释文档";
Map<String,Object> map = new HashMap<String,Object>();
map.put("title", title);
List<Map<String,String>> excelMapList = new ArrayList<Map<String,String>>();
Map<String,String> excelMapTemp = null;
for (Explain explain :explains) {
if(explain.getExp().equals(""))
{
explain.setExp("未在百科中爬取到此信息。");
}
excelMapTemp = new HashMap<String,String>();
excelMapTemp.put("excel.no1", explain.getWords());
excelMapTemp.put("excel.no2", explain.getExp());
excelMapList.add(excelMapTemp);
}
//模板存放位置
String demoTemplate = "C:/study/eclipse_work/WordsHot/WebContent/file/in.docx";
//生成文档存放位置
String targetPath = "C:/study/eclipse_work/WordsHot/WebContent/file/out.doc";
//初始化导出
WordExport export = new WordExport(demoTemplate);
try {
export.init();
} catch (IOException e) {
e.printStackTrace();
}
try {
export.export(map);
export.export(excelMapList,0);
export.generate(targetPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
推迟绑定时间
到目前为止已经讨论的两个战术分类经过了精心设计,以使实现修改所要求改变的模块的数量最少。我们的可修改性场景包括通过减少需要修改的模块的数量不能满足的两个元素——部署时间以及允许非开发人员进行修改。推迟绑定时间支持这两个场景,但需要提供额外的基础结构来支持后期绑定。暂时没有这两个场景。
前端部分,我将总的html文件和它所调用的jsp文件分开存放以方便管理。当需要增加新的功能页面时,在jsp文件夹里增加相应的文件并且在index.html中增加调用的代码即可。
后台数据处理部分,我将实体类、数据库连接、数据库数据处理三部分进行了划分。数据的获取与处理在dao层中执行,在其中会对util中的数据库连接方法以及bean中的实体类进行调用实现数据的读取、处理、存放。
一个系统的可用性主要是体现在这个系统的系统服务不中断运行时间占实际运行时间的比例,系统的伸缩性则是指在不改变系统软硬件设计,仅仅通过新增服务器的情况下,就能提升系统的处理能力,而系统的可扩展性是指该系统适应变化的能力。、
在连接数据库这一步骤中我们会用到如下代码:
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
通过这样的抛出异常机制,在数据库的连接中可以诊断异常,并且抛出。
性能最重要的就是时间和空间的合理划分。响应时间越短对于空间的要求往往也会非常高,不能顾此失彼,一个高性能的系统应该是两者都能兼顾到。
在编码的层面有如下实践。
资源需求:
1.减少一个事件流所用的资源
1.1:尽量避免随意使用静态变量。多使用局部变量
1.2:尽量使用基本数据类型代替对象。基本类型数据产生和处理都在栈中处理,对象是在堆中产生实例。
String str=“hello”;
String str =new String("hello");
1.3:使用移位代替乘除运算,左乘右除.<<,>>
1.4:尽量避免使用二维数组
1.5:频繁调用结构简单的函数设置为内联函数
2.减少处理事件的数量。
控制采样频率。
3.控制资源的使用
超时任务的返回设定。
应用:病毒动态演示时当某一天异常超时时,跳过渲染下一天地图。
jdk1.5自带的并发库中Future类。
Future类中重要方法包括get()和cancel()。get()获取数据对象,如果数据没有加载,就会阻塞直到取到数据,而 cancel()是取消数据加载。另外一个get(timeout)操作,表示如果在timeout时间内没有取到就失败返回,而不再阻塞。
安全性对于网站来说有与抵抗攻击有关的战术、与检测攻击有关的战术以及从攻击中恢复有关的战术。
从用户信息的安全性进行实践。
对原项目修改采用spring security来作为SpringBoot的身份验证和权限授予的插件。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
软件可测试性是指通过测试(通常是基于运行的测试)揭示软件缺陷的容易程度。软件的开发和测试是并行的,这就需要测试人员尽快找出软件的关键bug并且快速的修复他,这就需要程序员提高软件的可测试性,让软件在测试阶段更加方便明了的被测试,而不是结构混乱,难以测试,难以维护。
可测试性战术的目标是允许在完成软件开发的一个增量后,轻松地对软件进行测试。
易用性关注的是对用户来说完成某个期望任务的容易程度和系统所提供的用户支持的种类。
易用性(usability)是一种以使用者为中心的设计概念,易用性设计的重点在于让产品的设计能够符合使用者的习惯与需求。
以因特网网站的设计为例,希望让使用者在浏览的过程中不会产生压力或感到挫折,并能让使用者在使用网站功能时,能用最少的努力发挥最大的效能。
在网站的使用过程中比较人性化的操作是,登陆了一次后的一段时间内,可以免登录再次访问。具体的实现如下所示。
self.driver = webdriver.Chrome()
首先对可修改性战术进行分析。
可修改性战术的目标是控制实现、测试和部署变更的时间和成本。
一组修改性战术的目标是减少由某个变更直接影响的模块的数量。我们把这组可修改性战术称为“局部化修改”;
维持语义的一致性。语义的一致性指的是模块中责任之间的关系。目标是确保所有这些责任都能够协同工作,不需要过多地依赖其他模块。该目标是通过选择具有语义一致性的责任来实现的。耦合和内聚指标是度量语义一致性的尝试,但它们遗漏了变更的上下文。相反,应该根据一组预期的变更来度量语义一致性。其中的一个子战术就是“抽象通用服务”。通过专门的模块提供通用服务通常被视作支持重用。这是正确的,但抽象通用服务也支持可修改性。如果己经抽象出了通用服务,那么,对这些通用服务的修改只需要进行一次,而不需要在使用这些服务的每个模块中都进行修改。此外,对使用这些服务的模块的修改不会影响其他的用户。因此,该战术不仅支持局部化修改,而且还能够防止连锁反应。抽象通用服务的示例就是应用框架的使用和其他中间件软件的使用。
下面从数据库连接代码编写的层面对这一战术进行实践。
DBUtil.java
package hotWords.util;
import java.sql.*;
public class DBUtil {
public static Connection getConnection() throws ClassNotFoundException, SQLException {
String JDBC_DRIVER = "com.mysql.jdbc.Driver";
String DB_URL = "jdbc:mysql://localhost:3306/hotwords?useUnicode=true&characterEncoding=UTF-8&useSSL=false";
String USER = "root";