实验二 Java面向对象程序设计

实验内容

  1. 初步掌握单元测试和TDD

  2. 理解并掌握面向对象三要素:封装、继承、多态

  3. 初步掌握UML建模

  4. 熟悉S.O.L.I.D原则

  5. 了解设计模式

实验步骤

(一)单元测试

(1)三种代码

例子的需求:我们要在一个MyUtil类中解决一个百分制成绩转成“优、良、中、及格、不及格”五级制成绩的功能。

  • 伪代码:与具体编程语言无关,是从意图层面来解决问题的,是产品代码最自然的、最好的注释。上述例子的伪代码如下:

百分制转五分制:

如果成绩小于60,转成“不及格”

如果成绩在60与70之间,转成“及格”

如果成绩在70与80之间,转成“中等”

如果成绩在80与90之间,转成“良好”

如果成绩在90与100之间,转成“优秀”

其他,转成“错误”

  • 产品代码:用特定编程语言翻译。下面选定Java作为编程语言,产品代码如下:
public class MyUtil{
public static String percentage2FiveGrade(int grade){
//如果成绩小于60,转成“不及格”
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 "错误";
}
}
  • 测试代码:

测试正常情况的测试代码如下:

public class MyUtilTest {
public static void main(String[] args) {
//测试正常情况
if(MyUtil.percentage2FiveGrade(58) != "不及格")
System.out.println("test failed!");
else if(MyUtil.percentage2FiveGrade(68) != "及格")
System.out.println("test failed!");
else if(MyUtil.percentage2FiveGrade(78) != "中等")
System.out.println("test failed!");
else if(MyUtil.percentage2FiveGrade(88) != "良好")
System.out.println("test failed!");
else if(MyUtil.percentage2FiveGrade(98) != "优秀")
System.out.println("test failed!");
else
System.out.println("test passe```````
!");
}
}

运行结果如下:

测试不正常情况,例如输入为负分或大于100的成绩,测试代码如下:

public class MyUtilTest1 {
public static void main(String[] args) {
//测试出错情况
if(MyUtil.percentage2FiveGrade(-18) != "错误")
System.out.println("test failed 1!");
else if(MyUtil.percentage2FiveGrade(118) != "错误")
System.out.println("test failed 2!");
else
System.out.println("test passed!");
}
}

运行结果如下:

程序运行后发现错误,所以要修改代码,增加了边界定义和异常的定义,即增加了小于零和大于一百的判断语句,代码修改后如下:

public class MyUtil {
public static String percentage2fivegrade(int grade){
//如果成绩小于0,转成“错误”
if ((grade < 0))
return "错误";
//如果成绩小于60,转成“不及格”
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 "优秀";
//如果成绩大于100,转成“错误”
else
return "错误";
}
}

再次运行MyUtilTest1.java的运行结果如下所示:

测试边界情况,例如输入0,60,70,80,90,100这些边界成绩进行测试,测试代码如下:

public class MyUtilTest2 {
public static void main(String[] args) {
//测试边界情况
if(MyUtil.percentage2FiveGrade(0) != "不及格")
System.out.println("test failed 1!");
else if(MyUtil.percentage2FiveGrade(60) != "及格")
System.out.println("test failed 2!");
else if(MyUtil.percentage2FiveGrade(70) != "中等")
System.out.println("test failed 3!");
else if(MyUtil.percentage2FiveGrade(80) != "良好")
System.out.println("test failed 4!");
else if(MyUtil.percentage2FiveGrade(90) != "优秀")
System.out.println("test failed 5!");
else if(MyUtil.percentage2FiveGrade(100) != "优秀")
System.out.println("test failed 6!");
else
System.out.println("test passed!");
}
}

运行结果如下所示:

运行结果显示当输入边界成绩100时出现了一个Bug。所以修改代码,将成绩100添加到优秀的判断结果中,修改后的代码为:

public class MyUtil {
public static String percentage2FiveGrade(int grade){
//如果成绩小于0,转成“错误”
if ((grade < 0))
return "错误";
//如果成绩小于60,转成“不及格”
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 "优秀";
//如果成绩大于100,转成“错误”
else
return "t"
}
}

再次运行MyUtilTest2.java的运行结果如下所示:

测试结果显示代码符合预期要求,测试通过。

(2)TDD(Test Driven Devlopment, 测试驱动开发)

上述的例子是先写产品代码,再写测试代码,从测试过程中发现一些Bug,然后完善代码。而TDD则是先写测试代码,然后再写产品代码的开发方法。

TDD的一般步骤如下:

  • 明确当前要完成的功能,记录成一个测试列表
  • 测试代码编译不通过(没产品代码呢)
  • 测试通过
  • 对代码进行重构,并保证测试通过(重构下次实验练习)
  • 循环完成所有功能的开发

Java中有单元测试工具JUnit来辅助进行TDD,下面用TDD的方式把前面百分制转五分制的例子重写一次,体会一下有测试工具支持的开发的好处。

1.打开Eclipse,单击File->New->Java Project新建一个TDDDemo的Java项目。

2.在TDDDemo项目中,把鼠标放到项目名TDDDemo上,单击右键,在弹出的菜单中选定New->Source Folder新建一个测试目录test。如下图所示:

3.我们把鼠标放到test目录上,单击右键,在弹出的菜单中选定New->JUnit Test Case新建一个测试用例类MyUtilTest。

4.增加第一个测试用例testNormal,注意测试用例前一定要有注解@Test,测试用例方法名任意,并将上面的修改完成的代码MyUtil.java拷贝到src文件夹中,输入以下代码:

public class MyUtilTest extends TestCase {

	@Test
public void testNormal() {
assertEquals("不及格", MyUtil.percentage2FiveGrade(58));
assertEquals("及格", MyUtil.percentage2FiveGrade(68));
assertEquals("中等", MyUtil.percentage2FiveGrade(78));
assertEquals("良好", MyUtil.percentage2FiveGrade(88));
assertEquals("优秀", MyUtil.percentage2FiveGrade(98));
} }

5.把鼠标放到MyUtilTest.java上,单击右键,选择Run as->JUnit Test,运行结果如下:

(二)面向对象三要素

(1)抽象

抽象是抽出事物的本质特征而暂时不考虑他们的细节。程序设计中,抽象包括两个方面,一是过程抽象,二是数据抽象。"去粗取精、化繁为简、由表及里、异中求同"。

(2)封装、继承与多态

面向对象(Object-Oriented)的三要素包括:封装、继承、多态。面向对象的思想涉及到软件开发的各个方面,如面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程实现(OOP)。OOA根据抽象关键的问题域来分解系统,关注是什么(what)。OOD是一种提供符号设计系统的面向对象的实现过程,用非常接近问题域术语的方法把系统构造成“现实世界”的对象,关注怎么做(how),通过模型来实现功能规范。OOP则在设计的基础上用编程语言(如Java)编码。贯穿OOA、OOD和OOP的主线正是抽象。

  • 封装

    封装就是将数据与相关行为包装在一起以实现信息就隐藏。在Java中用类进行封装,比如一个Dog类:
public class Dog {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String bark(){
return "汪汪";
}
public String toString(){
return "The Dog's color is " + this.getColor() +", and it shouts "+ this.bark() + "!";`
`
}
}

封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度,从而带来模块化和信息隐藏的好处。例如Dog类通过使用类和访问控制隐藏了属性color,开放了接口setColor(),getColor(),bark()和toString。可以使用下面的代码来使用Dog类:

public class DogTest {
public static void main(String[] args){
Dog g = new Dog();
g.setColor("Blue");
getInfo(g);
} public static void getInfo(Dog d){
System.out.println(d.toString());
}
}

代码的运行结果如下图所示:

下面来使用StarUML工具来进行UML建模,首先创建一个Dog类,添加其对应的属性和方法,然后依创建一个Cat类,同时创建一个AnimalTest类来使用Dog类和Cat类。具体的UML图如下所示:

但是根据观察,Dog类和Cat类都有Color属性和相应的setter和getter方法,这明显违反了前面提到的DRY原则,可以通过继承解决这个问题,把Color属性和相应的setter和getter方法放到父类Animal中,修改后的UML如图所示:

继承指一个类的定义可以基于另外一个已经存在的类,即子类基于父类,从而实现父类代码的重用。继承是实现软件可重用的根基,是提高软件系统的可扩展性与可维护性的主要途径。

面向对象中允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式,我们称此现象为多态性。Java中,多态是指不同的类对象调用同一个签名的成员方法时将执行不同代码的现象。多态是面向对象程序设计的灵活性和可扩展性的基础。

所以针对上面获得的类图,可以进一步抽象,把Dog类中的bark()和Cat类中的meow()抽象成一个抽象方法shout(),Dog类和Cat类中覆盖这个方法,如以下UML图所示:

(三)设计模式

(1)S.O.L.I.D原则

  • SRP(Single Responsibility Principle,单一职责原则):决不要有一个以上的理由修改一个类。对象的改变仅仅依赖于单一职责的改变,基于软件设计中的高内聚性定义。

  • OCP(Open-Closed Principle,开放-封闭原则) :软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。OCP可以用抽象和继承、面向接口编程手段实现。

  • LSP(Liskov Substitusion Principle,Liskov替换原则):子类必须可以被其基类所代

    。使用指向基类的指针或引用的函数,必须能够在不知道具体派生类对象类型的情况下使用它。核心思想是父类型对象可以被子类型对象所取代。

  • ISP(Interface Segregation Principle,接口分离原则):客户不应该依赖他们并未使用的接口。

  • DIP(Dependency Inversionsion Principle,依赖倒置原则):高层模块不应该依赖于低层模块,二者都应该依赖于抽象

    。抽象不应该依赖于细节,细节应该依赖于抽象。在应用中通过依赖注入的方式实现解耦,重用低级模块,重用实现,解除依赖。

(2)模式与设计模式

模式是某外在环境下﹐对特定问题的惯用解决之道。模式必须使得问题明晰,阐明为什么用它来求解问题,以及在什么情况下有用,什么情况下不能起作用,每个模式因其重复性从而可被复用。模式中最重要的是设计模式。

(3)设计模式实示例

设计模式(design pattern)提供一个用于细化软件系统的子系统或组件,或它们之间的关系图,它描述通信组件的公共再现结构,通信组件可以解决特定语境中的一个设计问题。

设计模式有四个基本要素:

Pattern name:描述模式,便于交流,存档

Problem:描述何处应用该模式

Solution:描述一个设计的组成元素,不针对特例对特例

Consequence:应用该模式的结果权衡

同时也要了解设计模式可能会存在的过度设计问题以及如何避免它。

(四)练习:使用TDD的方式设计关实现复数类Complex。

(1)伪代码:

复数类Complex

复数 = 实部 + 虚部 i

属性:实部-shibu,虚部-xubu。

方法:

getShiBu(int shibu); 获取实部

getXuBu(int xubu);返回虚部

add(Complex c);实现复数相加

minus(Complex c);实现复数相减

print(Complex c);实现复数打印

复数相加 = (实部 + 实部)+(虚部 + 虚部)i

复数相减 = (实部 - 实部)+(虚部 - 虚部)i

复数打印:

虚部 > 0:"实部" + "+" + "虚部" + "i"

虚部 < 0:"实部" + "虚部" + "i"

虚部为0:"实部"

(2)测试代码:

public class ComplexTest extends TestCase{

    Complex a=new Complex();
Complex b=new Complex(1);
Complex c=new Complex(19,4);
Complex d=new Complex(5,-3); @Test
public void getXuBu() throws Exception {
assertEquals(0.0, a.getXuBu());
assertEquals(0.0, b.getXuBu());
assertEquals(4.0, c.getXuBu());
assertEquals(-3.0, d.getXuBu());
}
@Test
public void getShiBu() throws Exception {
assertEquals(0.0, a.getShiBu());
assertEquals(1.0, a.getShiBu());
assertEquals(19.0, a.getShiBu());
assertEquals(5.0, a.getShiBu());
}
@Test
public void add() throws Exception {
assertEquals("(1.0)", a.add(b).print());
assertEquals("(19.0+4.0i)", a.add(c).print());
assertEquals("(5.0-3.0i)", a.add(d).print());
}
@Test
public void minus() throws Exception {
assertEquals("(-1.0)", a.minus(b).print());
assertEquals("(-19.0-4.0i)", a.minus(c).print());
assertEquals("(-5.0+3.0i)", a.minus(d).print());
} }

测试结果如下图所示:

(3)产品代码:

public class Complex {

    private double shibu;

    private double xubu;

    public Complex() {

        this.shibu = 0.0;

        this.xubu = 0.0;

    }

    public Complex(double m) {

        this.shibu = m;

        this.xubu = 0.0;

    }

    public Complex(double m, double n) {

        this.shibu = m;

        this.xubu = n;

    }

    public double getShiBu(){

        return shibu;

    }

    public double getXuBu(){

        return xubu;

    }

    public Complex add(Complex c2) {

        return new Complex(shibu + c2.shibu, xubu + c2.xubu);

    }

    public Complex minus(Complex c2) {

        return new Complex(shibu - c2.shibu, xubu - c2.xubu);

    }

    public String print(){

        if(this.xubu>0){

            return "("+this.shibu+"+"+this.xubu+"i)";

        }

        else if(this.xubu<0){

        	return "("+this.shibu+this.xubu+"i)";

        }

        else{

        	return "("+this.shibu+")";

        }

    }

}

本次实验的 PSP(Personal Software Process) 时间:

步骤 耗时 百分比
需求分析 20min 14.2%
设 计 20min 14.2%
代码实现 40min 28.5%
测 试 40min 28.5%
分析总结 21min 14.6%

20145226夏艺华 《Java程序设计》实验报告二的更多相关文章

  1. 20145226夏艺华 JAVA预备作业1

    博客阅读总结 关于师生关系: 学生和老师之间我觉得关系时多元化的,不能拘泥于单独的一种关系:灌输与被灌输,教授与被教授--我认为,在不同的课程阶段,师生之间的关系都可以发生变化.前期的老师更像是一个指 ...

  2. 20145226夏艺华 《Java程序设计》实验报告四

    实验四 Android开发基础 实验内容 基于Android Studio开发简单的Android应用并部署测试 了解Android组件.布局管理器的使用 掌握Android中事件处理机制 Andro ...

  3. 20145226夏艺华《网络对抗》第一次实验拓展:shellcode注入+return-to-libc

    20145226夏艺华<网络对抗>第一次实验拓展:shellcode注入+return-to-libc shellcode注入实践 编写shellcode 编写shellcode已经在之前 ...

  4. 20145226夏艺华 网络对抗技术 EXP9 web安全基础实践

    20145226夏艺华 网络对抗技术 EXP9 web安全基础实践 !!!免考项目:wannacry病毒分析+防护 一.实验后回答问题 SQL注入攻击原理,如何防御 攻击原理 "SQL注入& ...

  5. 20145226夏艺华 网络对抗技术EXP8 WEB基础实践

    20145226夏艺华 网络对抗技术EXP8 WEB基础实践 实验问题回答 1.什么是表单? 表单在网页中主要负责数据采集功能.一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CG ...

  6. 20145226夏艺华 Exp6 信息搜集与漏洞扫描

    20145226夏艺华 Exp6 信息搜集与漏洞扫描 基础问题回答 哪些组织负责DNS,IP的管理? · 全球根服务器均由美国政府授权的ICANN统一管理,负责全球的域名根服务器.DNS和IP地址管理 ...

  7. 20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范

    20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范 实践内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法. · 简单应用SET工具建立冒名网站 · ett ...

  8. 20145226夏艺华 网络对抗技术EXP4 恶意代码分析

    20145226夏艺华 网络对抗技术EXP4 恶意代码分析(未完成版) 回答问题 (1)如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作 ...

  9. Python程序设计实验报告二:顺序结构程序设计(验证性实验)

      安徽工程大学 Python程序设计 实验报告 班级   物流191   姓名  崔攀  学号3190505136 成绩 日期     2020.3.22     指导老师       修宇 [实验 ...

随机推荐

  1. [T-ARA][괜찮아요][没关系]

    歌词来源: 没关系:http://music.163.com/#/song?id=22704448 没关系(Remix Ver.):http://music.163.com/#/song?id=258 ...

  2. 对Java中的异常的理解

    1.What is exception in Java? Java使用异常描述程序中可能出现的不正常情况.这个不正常可以是java认为的不正常,也可以是你主观上的出乎意料(自定义异常).总而言之,异常 ...

  3. window下安装好postgreSQL 9.3用cmd命令进入数据库(搞的我这个菜鸟只剩半条命)

    linux下基本没什么问题,但在window操作系统下比较麻烦. 需要添加环境变量path路径:C:\Program Files (x86)\PostgreSQL\9.3\bin 添加postgres ...

  4. java 相等测试与继承

    内容中包含 base64string 图片造成字符过多,拒绝显示

  5. 【[CQOI2018]交错序列】

    这个题简直有毒,\(O((a+b)^3logn)\)的做法不卡常只比\(O(2^n*n)\)多\(10\)分 看到\(a\)和\(b\)简直小的可怜,于是可以往矩阵上联想 发现这个柿子有些特殊,好像可 ...

  6. 【BBS】BBS论坛项目各个页面的工作流程图

    1论坛整体结构 2数据库结构 3登录页面 4论坛首页(显示各个板块) 5显示板块对应的内容 6文章内容页 7新增板块.发表文章.回复 8版面管理.用户管理.发帖排行

  7. Graph I - Graph

    Graph There are two standard ways to represent a graph G=(V,E)G=(V,E), where VV is a set of vertices ...

  8. pathinfo

    location ~ \.php { fastcgi_split_path_info ^((?U).+\.php)(/?.+)$; fastcgi_param PATH_INFO $fastcgi_p ...

  9. Codeforce Round #554 Div.2 D - Neko and Aki's Prank

    dp 找规律 我好菜啊好菜啊,完全没有思路. 在合法的括号序列中,左括号数一定大于等于右括号数的,所以我们可以先定义平衡度为左括号数-右括号数. 然后可以发现一个惊人的规律..就是在trie同一深度上 ...

  10. Selenium自动化测试之结果处理

    Selenium自动化测试之结果处理 一.断言 断言相当于性能测试中的检查点,常用断言种类很多,具体可以查看断言API:判断预期结果和实际结果是否一致,断言成功,程序继续处理,失败则终止运行,示例如下 ...