20175308 实验三《敏捷开发与XP实践》
实验要求
没有Linux基础的同学建议先学习《Linux基础入门(新版)》《Vim编辑器》 课程
- 完成实验、撰写实验报告,实验报告以博客方式发表在博客园,注意实验报告重点是运行结果,遇到的问题(工具查找,安装,使用,程序的编辑,调试,运行等)、解决办法(空洞的方法如“查网络”、“问同学”、“看书”等一律得0分)以及分析(从中可以得到什么启示,有什么收获,教训等)。报告可以参考范飞龙老师的指导
-
严禁抄袭,有该行为者实验成绩归零,并附加其他惩罚措施。
实验步骤
敏捷开发与XP(概念)
敏捷开发(Agile Development):
是一种以人为核心、迭代、循序渐进的开发方法。“敏捷流程”
是一系列价值观和方法论的集合。
XP(eXtreme Programming):
极限编程,是一种全新而快捷的软件开发方法。
XP准则:
沟通 :XP认为项目成员之间的沟通是项目成功的关键,并把沟通看作项目中间协调与合作的主要推动因素。
简单 :XP假定未来不能可靠地预测,在现在考虑它从经济上是不明智的,所以不应该过多考虑未来的问题而是应该集中力量解决燃眉之急。
反馈 :XP认为系统本身及其代码是报告系统开发进度和状态的可靠依据。系统开发状态的反馈可以作为一种确定系统开发进度和决定系统下一步开发方向的手段。
勇气:代表了XP认为人是软件开发中最重要的一个方面的观点。在一个软件产品的开发中人的参与贯穿其整个生命周期,是人的勇气来排除困境,让团队把局部的最优抛之脑后,达到更重大的目标。表明了XP对“人让项目取得成功”的基本信任态度。
XP的活动:编码、测试、倾听、设计
XP实践:项目成员用户成功执行XP活动的技术通过XP实践来呈现,包括编程、团队、过程相关的12条实践
我们关注其中的编码标准
,结对编程
,代码集体所有
,测试
,重构
。
(一)、编码标准
程序大多时候是给人看的,编程标准使代码更容易阅读和理解,甚至可以保证其中的错误更少。它包含:
- 具有说明性的名字
- 清晰的表达式
- 直截了当的控制流
- 可读的代码和注释
-
一致地使用某些规则和惯用法的重要性。
安装alibaba插件,进行扫描
- 打开idea,按照
Intellij IDEA
->Preferences
->Plugins
的顺序,打开插件搜索页面 搜索alibaba...找到如下插件
Alibaba Java Code Guidelines
并安装,重启IDEA后生效鼠标右键单击java文件名,点击
编码规约扫描
扫描结果如下:
alibaba将问题分为block
、critical
、major
三个级别,从扫描结果中我们看出,虽然代码运行正确,但是有着诸多不规范之处。我们之前往往追求代码的正确运行,而代码的规范与否却是我们没有注意过的。
对代码进行规范处理
代码标准中很重要的一项是如何给包、类、变量、方法等标识符命名。Java中的一般的命名规则有:
- 要体现各自的含义
- 包、类、变量用名词
- 方法名用动宾
- 包名全部小写,如:io,awt
- 类名第一个字母要大写,如:HelloWorldApp
- 变量名第一个字母要小写,如:userName
- 方法名第一个字母要小写:setName
其次,根据我们扫描结果的反馈信息,我们的代码中还存在着以下不规范之处:
- if、for等语句后的循环没有加括号(即使只有一条语句也依然要加大括号)
- 出现魔法值(代码中直接出现的数值)
- 没有给类添加创建者信息
- 覆盖方法没有添加@Override注解
对上述不规范的地方进行相应的修改,规范后的代码如下:
/**
* CodeStandard class
*
* @author Fomalhaut5308
* @date 2019/4/29
*/
public class CodeStandard {
public static void main(String[] args) {
int a = 20;
StringBuffer buffer = new StringBuffer();
buffer.append('S');
buffer.append("tringBuffer");
System.out.println(buffer.charAt(1));
System.out.println(buffer.capacity());
System.out.println(buffer.indexOf("tring"));
System.out.println("buffer = " + buffer.toString());
if (buffer.capacity() < a) {
buffer.append("1234567");
}
for (int i = 0; i < buffer.length(); i++) {
System.out.println(buffer.charAt(i));
}
}
}
Code菜单
本次实验中要求在IDEA中使用Code
->Reformate Code
将代码格式化。除了格式化功能外,Code菜单还具备以下功能:
几个重点的功能:
Surround With
包围代码,即将代码放入if、while等语句中Gengrate
快速生成Construct、Getter/Setter、toString等Optimize Imports
去掉未引用的包的导入声明
其他的功能个人觉得比较鸡肋。(具体功能可参考本链接)但一项功能的设定必有其存在的需求,在不需要这些功能的情景下你可能并不会感觉这些功能的必要,但是当你存在相应需求时,或许你会感谢这些快速高效的功能。
(二)、结对编程-版本控制
添加搭档
在码云中点击管理
->添加仓库成员
->邀请用户
,发送邀请链接后等待对方接受即可将搭档添加进码云仓库,双方互相进行如此操作。(下图仅供参考,这个是已经将搭档拉入项目后的截图)
下载Complex代码并加入代码测试
测试代码运行如下(实验二已有,这里不做赘述):
对搭档的代码添加至少三处重构
搭档原代码:
public class myComplex {
// 定义属性并生成getter,setter
double RealPart;
double ImagePart;
// 定义构造函数
myComplex(double R,double I){
RealPart=R;
ImagePart=I;
}
// 定义公有方法:加减乘除
myComplex ComplexAdd(myComplex a){
return new myComplex(RealPart+a.RealPart,ImagePart+a.ImagePart);
}
myComplex ComplexSub(myComplex a){
return new myComplex(RealPart-a.RealPart,ImagePart-a.ImagePart);
}
myComplex ComplexMulti(myComplex a){
return new myComplex(RealPart*a.RealPart-ImagePart*a.ImagePart,RealPart*a.ImagePart+a.RealPart*ImagePart);
}
myComplex ComplexDiv(myComplex a){
return new myComplex((RealPart*a.RealPart+ImagePart*a.ImagePart)/(a.RealPart*a.RealPart+a.ImagePart*a.ImagePart),(-RealPart*a.ImagePart+a.RealPart*ImagePart)/(a.RealPart*a.RealPart+a.ImagePart*a.ImagePart));
}
//Override Object
public boolean equals(Object obj){
if(obj==null)
return false;
else {
myComplex m = (myComplex) obj;
if (m.RealPart == RealPart && m.ImagePart == ImagePart)
return true;
else return false;
}
}
public String toString(){
String output;
if(ImagePart==0){
return output=RealPart+"";
}
else if(RealPart==0 && ImagePart!=0){
return output=ImagePart+"i";
}
else if(ImagePart>0) {
return output=RealPart+"+"+ImagePart+"i";
}
else return output=RealPart+""+ImagePart+"i";
}
}
重构一:修改类、方法、变量名使符合驼峰规则
myComplex
->MyComplex
ComplexXXXX
->complexXXXX
RealPart
->realPart
ImagePart
->imagePart
myComplex(double R,double I){
RealPart=R;
ImagePart=I;
}
到
MyComplex(double realPart,double imagePart){
this.realPart=realPart;
this.imagePart=imagePart;
}
重构二:添加重写toString方法的“@Override”注释
重构三:为if、while后单个语句添加大括号
在重构后,很多方法名为了符合驼峰规则
进行了修改,可能会导致测试代码中的方法名报错,因此最好先进行重构,再编写测试代码。
上传代码到搭档的码云
- 首先搭档将我邀请进他的仓库
- 本地克隆搭档的码云仓库
-
git remote add 搭档仓库链接
将本地仓库和远程仓库建立联系 -
git add .
、git commit -m "message"
、git push
上传至仓库中即可。
(三)、完成java密码学相关内容学习
凯撒密码
凯撒密码作为密码学中典型的单表代替密码,加密算法为E:c=m+3(mod26)。
在本实验中,我们将凯撒密码化为一般形式,即我们指定密钥用以加解密。为了方便显示,我将明文、密钥改为程序内部输出。
DES算法
DES算法创建对称密钥加解密步骤:
获取密钥
- 获取密钥生成器
KeyGenerator kg=KeyGenerator.getInstance("DESede");
- 初始化密钥生成器
kg.init(168);
(指定密钥的长度,如果该步骤省略的话,会根据算法自动使用默认的密钥长度) - 生成密钥
SecretKey k=kg.generateKey( );
- 通过对象序列化方式将密钥保存在文件中
运行代码后产生key1.dat,里面存储密钥用于加解密。
打印密钥文件中的内容
进行加密
- 从文件中获取密钥
FileInputStream f=new FileInputStream("key1.dat");
ObjectInputStream b=new ObjectInputStream(f);
Key k=(Key)b.readObject( );
2.创建密码器(Cipher对象)Cipher cp=Cipher.getInstance("DESede");
3.初始化密码器cp.init(Cipher.ENCRYPT_MODE, k);
4.获取等待加密的明文String s="Hello World!"; byte ptext[]=s.getBytes("UTF8");
5.执行加密并处理加密结果
byte ctext[]=cp.doFinal(ptext);
FileOutputStream f2=new FileOutputStream("SEnc.dat");
f2.write(ctext);
进行解密
- 获取密文
FileInputStream f=new FileInputStream("SEnc.dat");
int num=f.available();
byte[ ] ctext=new byte[num];
f.read(ctext);
2.获取密钥
FileInputStream f2=new FileInputStream("keykb1.dat");
int num2=f2.available();
byte[ ] keykb=new byte[num2];
f2.read(keykb);
SecretKeySpec k=new SecretKeySpec(keykb,"DESede");
SecretKeySpec类的构造器中第2个参数则指定加密算法。由于keykb1.dat中的密钥原来使用的是DESede算法,因此这里仍旧使用字符串“DESede”作为参数。
3.创建并初始化密码器
Cipher cp=Cipher.getInstance("DESede");
cp.init(Cipher.DECRYPT_MODE, k);
4.执行解密byte []ptext=cp.doFinal(ctext);
RSA算法
RSA算法分为公钥和私钥两个(一组)密钥,在密码学上已经提及,加密时以公钥加密,解密方用私钥进行解密。
获取密钥对
- 创建密钥对生成器
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
- 初始化密钥生成器
kpg.initialize(1024);
- 生成密钥对
KeyPair kp=kpg.genKeyPair( );
包含了公钥和私钥一组对信息 - 获取公钥和私钥
PublicKey pbkey=kp.getPublic( );
PrivateKey prkey=kp.getPrivate( );
进行加密
- 获取公钥
FileInputStream f=new FileInputStream("Skey_RSA_pub.dat");
ObjectInputStream b=new ObjectInputStream(f);
RSAPublicKey pbk=(RSAPublicKey)b.readObject( );
- 获取公钥的参数(e, n)
BigInteger e=pbk.getPublicExponent();
BigInteger n=pbk.getModulus();
- 获取明文整数(m)
String s="Hello World!";
byte ptext[]=s.getBytes("UTF8");
BigInteger m=new BigInteger(ptext);
- 执行计算
BigInteger c=m.modPow(e,n);
进行解密
- 读取密文
BufferedReader in=
new BufferedReader(new InputStreamReader(
new FileInputStream("Enc_RSA.dat")));
String ctext=in.readLine();
BigInteger c=new BigInteger(ctext);
- 获取私钥
FileInputStream f=new FileInputStream("Skey_RSA_priv.dat");
ObjectInputStream b=new ObjectInputStream(f);
RSAPrivateKey prk=(RSAPrivateKey)b.readObject( );
- 获取私钥的参数(d, n)
BigInteger d=prk.getPrivateExponent( );
BigInteger n=prk.getModulus( );
- 执行计算
BigInteger m=c.modPow(d,n);
- 计算明文整型数对应的字符串
byte[] mt=m.toByteArray();
for(int i=0;i<mt.length;i++){
System.out.print((char) mt[i]);
}
实验总结与感想
编码标准阶段
在实验的这一阶段主要是尝试运用alibaba编码规约扫描
插件,惊奇的发现原来平日里自己写的代码原来有这么多不规范的地方。娄老师在博客中说代码是给人读的,我们在敲代码写程序的时候,不是简单的将一件事完成就算结束了,而是要在正确的基础上规范自己的书写和标准,即方便其他人的阅读也能方便自己的纠错。虽然规范与否短时间内看不出什么非常明显的优势,但是养成一个良好的习惯最终一定会润物无声地对我们如同臂助。
同时,我第一次使用了code菜单栏,发现了许多简单的功能,运用它们省时省力,可以节省许多不必要的时间。我们使用IDEA本就是源于它的高效,如果对这些功能不加以运用无异于买椟还珠。所以在今后的学习中,我们应该不断学习IDE的使用,掌握它的功能,尤其要熟练那些帮助非常的的功能的快捷键。
结对阶段
结对的思想早在结对编程任务就已有学习。在此次实验中我的搭档依然选择了结对伙伴。在结对过程中难免会遇到意见不同的情况,但是有一句话说的好有时候,不同意见也是健康关系的重要组成部分,意见不同也可以看作是找寻最佳方案的良机,大家都坚持自己的思想,势必会最终得到一个更加完美的解决,值得把握。
密码学相关内容学习阶段
在这一阶段,我们学习了许多密码学课上学到的算法的编程实现,例如凯撒密码、DES算法、RSA算法。我们在学校一科一科的学习,往往自动的将它们割裂开来。学密码学是密码学,学数据结构是数据结构,稍微结合一点编程的操作就觉得难比登天。其实这是不对的。娄老师倡导我们做中学,数据结构张岩老师说:计算机毫无智能可言,娄老师在博客中引用了侯捷老师的方法论会用,明理,扩展
,明理之前要会用。其实把这几话结合起来,我觉得就是这项任务的用意。在学一样东西,首先尝试使用,再深入理解达到明理。我们在学密码学,可以尝试用编程的方法使用它,计算机做的是你教给它的东西,它成功的实现了一个算法,就代表你的思路正确,你就可以在正确的思路上深入挖掘。前几次的密码学实验都可以用java实现,因为现阶段中,java编程也是我们要学习的内容。