一、实验内容

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

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

3. 初步掌握UML建模

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

5. 了解设计模式

二、实验步骤

(一)单元测试

(1) 三种代码

用程序解决问题时,要会写三种码:伪代码、产品代码、测试代码

我们通过一个例子说明如何写这三种代码。

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

1.我们可以通过伪代码这样解决:

   百分制转五分制:
如果成绩小于60,转成“不及格”
如果成绩在60与70之间,转成“及格”
如果成绩在70与80之间,转成“中等”
如果成绩在80与90之间,转成“良好”
如果成绩在90与100之间,转成“优秀”
其他,转成“错误”

2.产品代码:翻译好的MyUtil.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 "错误";
}
}

3.测试代码:MyUtilTest.java的测试模块,代码如下:

public class MyUtilTest {
public static void main(String[] args) {
// 百分制成绩是50时应该返回五级制的“不及格”
if(MyUtil.percentage2fivegrade(50) != "不及格")
System.out.println("test failed!");
else
System.out.println("test passed!");
}
}

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

先写测试代码,然后再写产品代码,从而写出来的代码就是正确的。

这种先写测试代码,然后再写产品代码的开发方法叫“测试驱动开发”(TDD)。TDD的一般步骤如下:

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

打开Eclipse,单击File->New->Java Project新建一个TDDDemo的Java项目,如下图:

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

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

我们增加第一个测试用例testNormal,注意测试用例前一定要有注解@Test,测试用例方法名任意,输入以下代码:

import org.junit.Test;
import junit.framework.TestCase;
public class MyUtilTest extends TestCase {
@Test
public void testNormal() {
assertEquals("不及格", MyUtil.percentage2fivegrade(55));
assertEquals("及格", MyUtil.percentage2fivegrade(65));
assertEquals("中等", MyUtil.percentage2fivegrade(75));
assertEquals("良好", MyUtil.percentage2fivegrade(85));
assertEquals("优秀", MyUtil.percentage2fivegrade(95));
}
}

输入完毕,Eclipse中如下图所示:

问号是因为环境中复制粘贴后中文改变。图中的红叉说明代码存在语法错误,原因很简单,MyUtil类还不存在,类中的percentage2fivegrade方法也不存在,我们在TDDDemosrc目录中新建一个MyUtil的类,并实现percentage2fivegrade方法,如下图所示:

大家可以看到现在测试代码没有语法错误了,我们把鼠标放到MyUtilTest.java上,单击右键,选择Run as->JUnit Test,

测试结果出现了一个红条(red bar),说明测试没通过,红条上面汇总了测试情况,运行了一个测试,没有错误,一个测试没通过。下面原因说的也很清楚:测试代码第十行传入55时,期望结果是“不及格”,代码返回了“错误”,修改MyUtil.Java吧,输入以下代码:

再运行测试,如下图所示:

测试结果出现了一个绿条(green bar),说明测试通过了。TDD的目标是"Clean Code That Works",TDD的slogan是"Keep the bar green, to Keep the code clean",大家体会一下。

TDD的编码节奏是:

  • 增加测试代码,JUnit出现红条
  • 修改产品代码
  • JUnit出现绿条,任务完成

我们增加一个测试异常情况的用例testException。

增加测试边界情况的用例

(二)面向对象三要素

(1)抽象

"去粗取精、化繁为简、由表及里、异中求同"

程序设计中,抽象包括两个方面,一是过程抽象,二是数据抽象。

(2)封装、继承与多态

封装实际上使用方法(method)将类的数据隐藏起来,控制用户对类的修改和访问数据的程度,从而带来模块化(Modularity)和信息隐藏(Information hiding)的好处;接口(interface)是封装的准确描述手段。 Dog类通过使用类和访问控制(private,public)隐藏了属性color,开放了接口setColor(),getColor(),bark()toStringDog类是一个模块,我们可以通过下面的代码使用它,测试代码与运行结果如下:

我们可以用UML中的类图来描述类Dog,首先我们在实验楼的环境中打开shell,在命令行中输入umbrello,打开UML建模软件umbrello

我们把鼠标放到Dog类上,单击右键,选择Properties,在弹出的对话框中的Display中去掉Public Only选项,如下图:

我们把鼠标放到Dog类上,单击右键,选择New->Attribute,在弹出的对话框中的填好Type,Name,并选好Visibility,如下图:

我们把鼠标放到Dog类上,单击右键,选择New->Operation,在弹出的对话框中的填好Type,Name,并选好Visibility,如下图:

我们可以看到,在UML 里,一个类的属性能显示它的名字,类型,初始化值,属性也可以显示private,public,protected。 类的方法能显示它们的方法名,参数,返回类型,以及方法的private,public,protected属性。其中:

  • +表示public
  • #表示 protected
  • -表示 private

建立Cat类时的测试类如以下UML图所示:

我们看到Dog类和Cat类都有Color属性和相应的setter和getter方法,明显违反了前面提到的DRY原则,我们可以通过继承解决这个问题,把Color属性和相应的setter和getter方法放到父类Animal中,如以下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图所示:

大家注意UML类图中的Animal类中的shout()方法是抽象方法,是斜体的,Animal类是抽象类,也是斜体的。 对应的代码如下:

public abstract class Animal {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public abstract String shout();
}
public class Dog extends Animal{
public String shout(){
return "汪汪";
}
public String toString(){
return "The Dog's color is " + this.getColor() +", and it shouts "+ this.shout() + "!";
}
}
public class Cat extends Animal{
public String shout(){
return "喵喵";
}
public String toString(){
return "The Cat's color is " + this.getColor() +", and it shouts "+ this.shout() + "!";
}
}

(三)设计模式初步

(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.
  • 软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。

LSP的内容是:

  • Subtypes must be substitutable for their base types
  • Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it
  • 子类必须可以被其基类所代
  • 使用指向基类的指针或引用的函数,必须能够在不知道具体派生类对象类型的情况下使用它

ISP的内容是:

  • Clients should not be forced to depend upon interfaces that they do not use
  • 客户不应该依赖他们并未使用的接口

DIP的内容是:

  • High level modules should not depend upon low level modules. Both should depend upon abstractions
  • Abstractions should not depend upon details. Details should depend upon abstractions
  • 高层模块不应该依赖于低层模块。二者都应该依赖于抽象
  • 抽象不应该依赖于细节。细节应该依赖于抽象

(2)模式与设计模式

模式是某外在环境(Context) 下﹐对特定问题(Problem)的惯用解决之道(Solution)。模式必须使得问题明晰,阐明为什么用它来求解问题,以及在什么情况下有用,什么情况下不能起作用,每个模式因其重复性从而可被复用,本身有自己的名字,有可传授性,能移植到不同情景下。模式可以看作对一个问题可复用的专家级解决方法。 计算机科学中有很多模式:

  • GRASP模式
  • 分析模式
  • 软件体系结构模式
  • 设计模式:创建型,结构型,行为型
  • 管理模式: The Manager Pool 实现模式
  • 界面设计交互模式

这里面最重要的是设计模式,在面向对象中设计模式的地位可以和面向过程编程中的数据结构的地位相当。

(3)设计模式实示例

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

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

  • Pattern name:描述模式,便于交流,存档
  • Problem:描述何处应用该模式
  • Solution:描述一个设计的组成元素,不针对特例
  • Consequence:应用该模式的结果和权衡(trade-offs)

我们先要学习的是GOF的23个设计模式:

Java类库中大量使用设计模式:

  • Factory:java.util.Calendar
  • Compsite:java.awt.Container
  • Decorator:java I/0
  • Iterator:java.util.Enumeration
  • Strategy:java.awt.LayoutManager

我们通过例子来学习一个设计模式(抽象工厂模式),并了解设计模式可能会存在的过度设计问题以及如何避免它。 我们设计了一个文档系统,如下图UML类图所示:

对应的代码如下:

class Integer {
int value;
public Integer(){
value=100;
}
public void DisplayValue(){
System.out.println(value);
}
}
class Document {
Integer pi;
public Document(){
pi = new Integer();
}
public void DisplayData(){
pi.DisplayValue();
}
}
public class MyDoc{
static Document d;
public static void main(String [] args) {
d = new Document();
d.DisplayData();
}
}

客户如果要求系统支持Float类,这是一个合理的要求,要支持Float类,Document类要修改两个地方,这违反了OCP原则,使用多态可以解决部分问题:

对应的代码如下:

abstract class Data{
public abstract void DisplayValue();
}
class Integer extends Data {
int value;
Integer(){
value=100;
}
public void DisplayValue(){
System.out.println(value);
}
}
class Document {
Data pd;
Document() {
pd=new Integer();
}
public void DisplayData(){
pd.DisplayValue();
}
}
public class MyDoc {
static Document d;
public static void main(String[] args) {
d = new Document();
d.DisplayData();
}
}

要支持Float类,Document类要修改构造方法,这还违反了OCP原则。封装、继承、多态解决不了问题了,这时需要设计模式了:

抽象工厂模式应用如下:

对应代码如下:

// Server Classes
abstract class Data {
abstract public void DisplayValue();
}
class Integer extends Data {
int value;
Integer() {
value=100;
}
public void DisplayValue(){
System.out.println (value);
}
}
// Pattern Classes
abstract class Factory {
abstract public Data CreateDataObject();
}
class IntFactory extends Factory {
public Data CreateDataObject(){
return new Integer();
}
}
//Client classes
class Document {
Data pd;
Document(Factory pf){
pd = pf.CreateDataObject();
}
public void DisplayData(){
pd.DisplayValue();
}
}
//Test class
public class MyDoc {
static Document d;
public static void main(String[] args) {
d = new Document(new IntFactory());
d.DisplayData();
}
}

我们看到通过增加了一层抽象层使代码符合了OCP原则。代码有良好的可扩充性、可维护性,代价是代码多了,效率变低下了。 设计模式初学者容易过度使用它们,导致过度设计,也就是说,遵守DRY和OCP当然好,但会出现YAGNI(You aren't gonna need it, 你不会需要它)问题。 DRY原则和YAGNI原则并非完全兼容。前者追求"抽象化",要求找到通用的解决方法;后者追求"快和省",意味着不要把精力放在抽象化上面,因为很可能"你不会需要它"。怎么平衡呢?有一个Rule of three (三次原则):第一次用到某个功能时,你写一个特定的解决方法;第二次又用到的时候,你拷贝上一次的代码(违反了DRY);第三次出现的时候,你才着手"抽象化",写出通用的解决方法。 设计模式学习先参考一下《深入浅出设计模式》,这本书可读性非常好。

除SOLID原则外还有很多其它的面向对象原则。如:

  • "组合替代继承":这是说相对于继承,要更倾向于使用组合;
  • "笛米特法则":这是说"你的类对其它类知道的越少越好";
  • "共同封闭原则":这是说"相关类应该打包在一起";
  • "稳定抽象原则":这是说"类越稳定,越应该由抽象类组成";

当然,这些原则并不是孤立存在的,而是紧密联系的,遵循一个原则的同时也就遵循了另外一个或多个原则;反之,违反了其中一个原则也很可能同时就违反了另外一个或多个原则。 设计模式是这些原则在一些特定场景的应用结果。因此,可以把设计模式看作"框架",把OOD原则看作"规范"。 在学习设计模式的过程中,要经常性的反思,这个设计模式体现了面向对象设计原则中的哪个或哪一些原则。

(四)练习

1使用TDD的方式设计关实现复数类Complex。

(1)伪代码

  实现复数类:

  建立复数,实部和虚部;

复数相加,输出复数;

复数相减,输出复数。

(2)产品代码

(3)测试代码

2.实验报告中统计自己的PSP(Personal Software Process)时间

步骤

耗时

百分比

需求分析

5分钟

5.9%

设计

10分钟

11.6%

代码实现

50分钟

59%

测试

20分钟

23.5%

分析总结

3. 实现要有伪代码,产品代码,测试代码。

三、实验中遇到的问题

1.第一次Green bar没出来

MyUtil中的“zhong deng ”多了一个空格,导致应该有的结果和实际结果不一样

2.点击类图不会出现圣诞框

网速问题。

3.建完Dog类后再次建立Cat出现以下状况

解决方法:点击左上角new再次建立

4.练习中

虽然过程中没出现表示错误的红叉,但是Method test()不能是静态

四、实验总结

1.单元测试的好处

(1)减少bug

(2)提高代码质量

(3)减少后期维护的精力和时间

(4)优化设计

2.java有好多我还不知道的巧妙的地方。这次特别是对面向对象的编程思想和风格有了进一步的认识和体会。同时,因看到green bar而带来的成就感让我对编程更加感兴趣。实验楼中步骤很详细,我也在这些过程中树立了敢于攻坚的信心。

java实验报告二的更多相关文章

  1. Java实验报告二:Java面向对象程序设计

    Java实验报告二:Java面向对象程序设计                                                                               ...

  2. 20145203盖泽双:Java实验报告二

    Java实验报告二:Java面向对象程序设计 实验要求: 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验内容 ...

  3. 20135337朱荟潼Java实验报告二

    20135337朱荟潼 实验二 Java面向对象程序设计 一.实验内容 1. 初步掌握单元测试和TDD 2. 理解并掌握面向对象三要素:封装.继承.多态 3. 初步掌握UML建模 4. 熟悉S.O.L ...

  4. 20145221 《Java程序设计》实验报告二:Java面向对象程序设计

    20145221 <Java程序设计>实验报告二:Java面向对象程序设计 实验要求 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O. ...

  5. Java实验报告五:Java网络编程及安全

    Java实验报告五:Java网络编程及安全                                                                               ...

  6. 20135231 JAVA实验报告三:敏捷开发与XP实践

    ---恢复内容开始--- JAVA实验报告三:敏捷开发与XP实践 20135231 何佳 实验内容 1. XP基础 2. XP核心实践 3. 相关工具 实验要求 1.没有Linux基础的同学建议先学习 ...

  7. 第三次Java实验报告

    Java实验报告 班级 计科二班 学号20188437 姓名 何磊 完成时间 2019/9/22 评分等级 实验三 String类的应用 实验目的 掌握类String类的使用: 学会使用JDK帮助文档 ...

  8. Java实验报告&&课程报告

    Java实验报告 班级 计算机科学与技术二班 学号 20188450 姓名 李代传 完成时间 2019/9/19 评分等级 实验二 Java简单类与对象 实验目的 掌握类的定义,熟悉属性.构造函数.方 ...

  9. 第二次Java实验报告

    Java实验报告 班级 计科二班 学号 20188437 姓名 何磊 完成时间 2019/9/12 评分等级 实验二 Java简单类与对象 实验目的 掌握类的定义,熟悉属性.构造函数.方法的作用,掌握 ...

随机推荐

  1. 关于MVC开发时,无法找到area的问题记录

    解决方法: 检查area=admin 的dll是否生成,一般都是admin域生成dll导致

  2. C#实现的协同过滤算法

    using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace SlopeOn ...

  3. [2018-12-15] Hello World!

    这个blog以后就用来发oi相关的算法与数据结构了 还可能想学习一点web前端的知识和一些与计算机有关的软件和技术 可能有空大概会试试搭建blog以及一些各种软件和c++以外的玩意

  4. c# winform 自动升级

    winform自动升级程序示例源码下载  客户端思路: 1.新建一个配置文件Update.ini,用来存放软件的客户端版本: [update] version=2011-09-09 15:26 2.新 ...

  5. 2.2.3 TableLayout(表格布局)

    3.如何确定行数与列数 ①如果我们直接往TableLayout中添加组件的话,那么这个组件将占满一行!!! ②如果我们想一行上有多个组件的话,就要添加一个TableRow的容器,把组件都丢到里面! ③ ...

  6. Linux备份压缩命令

    gzip 命令 把/home/chenjialins目录下的familyA目录下所有文件压缩成.gz文件cd /home/chenjialinstar -cvf /home/chenjialins/f ...

  7. Python基础(10)——类进阶(静态方法、类方法、属性方法)

    以下方法为高级方法,一般来说没什么卵用 1.静态方法 @staticmethod 相当于把类内的函数从类内独立出来,只是名义上归类管,实际上不可以调用类内的变量和函数 通过@staticmethod装 ...

  8. OpenCV——霍夫变换(直线检测、圆检测)

    x #include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namesp ...

  9. nginx配置收集

    同个服务,分别读取不同借口 location /xibao/service_api/ { if ($request_uri ~ ^/xibao/(.*)) { set $xibao_data_url ...

  10. JAVA框架Struts2--配置讲解

    一.配置讲解: <struts> <package name="oop" namespace="/" extends="struts ...