北京电子科技学院(BESTI)
实 验 报 告
课程:java程序设计 班级:1351 姓名:陈民禾 学号:20135124
成绩: 指导教师:娄佳鹏 实验日期:2015/05/03
实验密级: 预习程度: 实验时间:下午3:00-5:00
仪器组次: 必修/选修:选修 实验序号:02
实验名称: java面向对象程序设计
一.初步掌握单元测试和TDD
伪代码:
伪代码:
百分制转五分制:
如果成绩小于60,转成“不及格”
如果成绩在60与70之间,转成“及格”
如果成绩在70与80之间,转成“中等”
如果成绩在80与90之间,转成“良好”
如果成绩在90与100之间,转成“优秀”
其他,转成“错误”
只有一组输入的测试是不充分的,我们把一般情况都测试一下,代码如下:
我们不能只测试正常情况,下面看看异常情况如何,比如输入为负分或大于100的成绩,代码如下:
代码修改如下,因为出现错误:
package javalab2;
public class MyUtil {
public static String percentage2fivegrade(int grade)
{
//如果成绩小于0,转成“错误”
if ((grade < 0))
return "错误";
else if(grade<60)
return "不及格";
//如果成绩在60与70之间,转成"及格"
else if(grade<70)
return "及格";
//如果成绩在70与80之间,转成"中等"
else if(grade<80)
return"中等";
//如果成绩在80与90之间,转成"良好"
else if(grade<90)
return"良好";
//如果成绩在90与100之间,转成"优秀"
else if(grade<100)
return"优秀";
//其他,转成"错误"
else
return"错误";
}
}
测试还不够,一般代码在边界处最容易出错,我们还没有测试边界情况,我们对输入为“0,60,70,80,90,100”这些边界情况进行测试的代码如下:
在100处有错误,对代码进行修改:
package javalab2;
public class MyUtil {
public static String percentage2fivegrade(int grade)
{
//如果成绩小于0,转成“错误”
if ((grade < 0))
return "错误";
else if(grade<60)
return "不及格";
//如果成绩在60与70之间,转成"及格"
else if(grade<70)
return "及格";
//如果成绩在70与80之间,转成"中等"
else if(grade<80)
return"中等";
//如果成绩在80与90之间,转成"良好"
else if(grade<90)
return"良好";
//如果成绩在90与100之间,转成"优秀"
else if(grade<=100)
return"优秀";
//其他,转成"错误"
else
return"错误";
}
}
再次进行运行,结果如下:
2.TDD即为Test Driven Development
我们先写产品代码,再写测试代码,通过测试发现一些bugs,来提高代码质量
建立文件夹类包及其过程
我们可以用UML中的类图来描述类Dog
,首先我们在实验楼的环境中打开shell,在命令行中输入umbrello
,打开UML建模软件umbrello
,如下图所示
我们可以看到,在UML 里,一个类的属性能显示它的名字,类型,初始化值,属性也可以显示private,public,protected。 类的方法能显示它们的方法名,参数,返回类型,以及方法的private,public,protected属性。其中:
- +表示public
- #表示 protected
- -表示 private
注意:UML类图要展示类之间的静态关系,AnimalTest
类依赖Dog
类和Cat
类,UML中依赖用带箭头的直线表示。
对应的测试代码和运行结果如下图所示:
请大家注意UML类图中继承的表示法,是用一个带三角的直线指向父类,通过继承,我们消除了Dog
类和Cat
类中的重复代码,符合DRY
的要求。
继承指一个类的定义可以基于另外一个已经存在的类,即子类基于父类,从而实现父类代码的重用。既存类称作基类、超类、父类(base class、super class、parent class),新类称作派生类、继承类、子类(derived class、inherited class、child class)。继承关系表达了”Is a kind of“的关系,称为“ISA”关系。继承的关键在于确认子类为父类的一个特殊类型
。继承是实现软件可重用的根基,是提高软件系统的可扩展性与可维护性的主要途径。
如上面所示,以封装为基础,继承可以实现代码复用,需要注意的是,继承更重要的作用是实现多态。
面向对象中允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式,我们称此现象为多态性。Java中,多态是指不同的类对象调用同一个签名的成员方法时将执行不同代码的现象。多态是面向对象程序设计的灵活性和可扩展性的基础。
我们再看看上一个类图,我们可以进一步抽象,把Dog
类中的bark()
和Cat
类中的meow()
抽象成一个抽象方法shout()
,Dog
类和Cat
类中覆盖这个方法,如以下UML图所示:
设计模式初步
(1)S.O.L.I.D原则
面向对象三要素是“封装、继承、多态”,任何面向对象编程语言都会在语法上支持这三要素。如何借助抽象思维用好三要素特别是多态还是非常困难的,S.O.L.I.D
类设计原则是一个很好的指导:
- SRP(Single Responsibility Principle,单一职责原则)
- OCP(Open-Closed Principle,开放-封闭原则)
- LSP(Liskov Substitusion Principle,Liskov替换原则)
- ISP(Interface Segregation Principle,接口分离原则)
- DIP(Dependency Inversion Principle,依赖倒置原则)
OCP
是OOD中最重要的一个原则,OCP
的内容是:
- software entities (class, modules, function, etc.) should open for extension,but closed for modification.
- 软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。
思考题
complex 代码如下:
public class MainClass { // 用于测试复数类
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("请用户输入第一个复数的实部和虚部:");
Complex data1 = new Complex();
System.out.println("请用户输入第二个复数的实部和虚部:");
Complex data2 = new Complex();
// 以下分别为加减乘除
Complex result_add = data1.add(data2);
Complex result_sub = data1.sub(data2);
Complex result_mul = data1.mul(data2);
Complex result_div = data1.div(data2);
result_add.print();
result_sub.print();
result_mul.print();
result_div.print();
}
}
java.util.Scanner;
public class Complex { // 复数类
double real; // 实部
double image; // 虚部
Complex(){ // 不带参数的构造方法
Scanner input = new Scanner(System.in);
double real = input.nextDouble();
double image = input.nextDouble();
Complex(real,image);
}
private void Complex(double real, double image) { // 供不带参数的构造方法调用
// TODO Auto-generated method stub
this.real = real;
this.image = image;
}
Complex(double real,double image){ // 带参数的构造方法
this.real = real;
this.image = image;
}
public double getReal() {
return real;
}
public void setReal(double real) {
this.real = real;
}
public double getImage() {
return image;
}
public void setImage(double image) {
this.image = image;
}
Complex add(Complex a){ // 复数相加
double real2 = a.getReal();
double image2 = a.getImage();
double newReal = real + real2;
double newImage = image + image2;
Complex result = new Complex(newReal,newImage);
return result;
}
Complex sub(Complex a){ // 复数相减
double real2 = a.getReal();
double image2 = a.getImage();
double newReal = real - real2;
double newImage = image - image2;
Complex result = new Complex(newReal,newImage);
return result;
}
Complex mul(Complex a){ // 复数相乘
double real2 = a.getReal();
double image2 = a.getImage();
double newReal = real*real2 - image*image2;
double newImage = image*real2 + real*image2;
Complex result = new Complex(newReal,newImage);
return result;
}
Complex div(Complex a){ // 复数相除
double real2 = a.getReal();
double image2 = a.getImage();
double newReal = (real*real2 + image*image2)/(real2*real2 + image2*image2);
double newImage = (image*real2 - real*image2)/(real2*real2 + image2*image2);
Complex result = new Complex(newReal,newImage);
return result;
}
public void print(){ // 输出
if(image > 0){
System.out.println(real + " + " + image + "i");
}else if(image < 0){
System.out.println(real + "" + image + "i");
}else{
System.out.println(real);
}
}
}
(四)练习
统计的PSP(Personal Software Process)时间
步骤 |
耗时(min) |
百分比 |
需求分析 |
10~15 |
15% |
设计 |
25~30 |
25% |
代码实现 |
35~40 |
40% |
测试 |
5~10 |
5% |
分析总结 |
10~15 |
15%
|
伪代码:
1,设计一个复数类,将复数的实部和虚部分别用变量表示。
2,通过实部和虚部分别相加减实现复数的相加减。
3,按照复数的格式将复数打印出来。
产品代码:
package shiyan2_20135220;
public class ComplexDemo{
double re,im;
//构造函数
ComplexDemo(){
this.re=0;
this.im=0;
}
ComplexDemo(double re,double im){
this.re=re;
this.im=im;
}
//设置实部
public void setRealPart(double re){
this.re = re;
}
//设置虚部
public void setImaginaryPart(double im){
this.im = im;
}
//获取实部
public double getRealPart(){
return this.re;
}
//获取虚部
public double getImaginaryPart(){
return this.im;
}
//实现复数的加减
public void add(ComplexDemo c){
this.re = this.re + c.re;
this.im = this.im + c.im;
}
public void minus(ComplexDemo c){
this.re = this.re - c.re;
this.im = this.im - c.im;
}
//打印复数
public String toString(){
if(this.im!=0)
return "复数的值为:"+this.getRealPart()+"+"+this.getImaginaryPart()+"i";
else
return "复数的值为:"+this.getRealPart();
}
}
单元测试的好处:
1、单元测试之后,知道自己代码问题。
2、知道哪里错了方便改
3.对代码的熟悉
实验体会:
这次实验花费了较长的时间,内容虽然有些多,老师辛苦写的实验内容让我们熟悉,多学习java知道了写代码的正确步骤,有三种代码要写。
还认识到了单元测试的重要性,并且自身使用TDD的方式设计关实现复数类Complex知道了怎么回事,初步掌握单元测试和TDD,。除此之外,还初步掌握了UML建模,熟悉了S.O.L.I.D原则,初步了解了设计模式。