20135101曹钰晶

实验内容

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

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

3. 初步掌握UML建模

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

5. 了解设计模式

实验要求

1.没有Linux基础的同学建议先学习《Linux基础入门(新版)》《Vim编辑器》 课程

2.完成实验、撰写实验报告,实验报告以博客方式发表在博客园,注意实验报告重点是运行结果,遇到的问题(工具查找,安装,使用,程序的编辑,调试,运行等)、解决办法(空洞的方法如“查网络”、“问同学”、“看书”等一律得0分)以及分析(从中可以得到什么启示,有什么收获,教训等)。报告可以参考范飞龙老师的指导

3. 严禁抄袭,有该行为者实验成绩归零,并附加其他惩罚措施。

4. 请大家先在实验楼中的~/Code目录中用自己的学号建立一个目录,代码和UML图要放到这个目录中,截图中没有学号的会要求重做,然后跟着下面的步骤练习

实验步骤:

(一)单元测试

(1) 三种代码

(1)三种代码

     伪代码

     产品代码

     测试代码

例:

   需求:在一个MyUtil类中解决一个百分制成绩转成“优、良、中、及格、不及格”

五级制成绩的功能。

  先写伪代码:

 /*

 百分制转五分制:

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

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

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

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

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

 其他,转成“错误”

 */

   产品代码:

public class MyUtil {

public static String percentage2fivegrade(int grade){

    //如果成绩小于0,转成“错误”

    if (grade < 0)

        return "错误";

    //如果成绩小于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 "优秀";

    //如果成绩大于100,转成“错误”

            else

                return "错误";

    //其他,转成“错误”;

}

}

  

   测试代码:(针对MyUtil.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!");

}

}

 

测试用例(TestCase),是为某个特殊目标而编制的一组测试输入、执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求。

 

 

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

先写测试代码,再写产品代码

一般步骤:

  明确当前要完成的功能,记录一个测试列表;

  快速完成编写针对此功能的测试用例;

  测试代码编译不通过(没产品代码);

  编写产品代码;

  测试通过;

  对代码进行重构,并保证测试通过(重构下次实验练习);

  循环完成所有功能的开发;

 

TDD的目标:Clean Code That Works.

TDD的slogan是:Keep the bar green,to keep the code clean.

TDD的编码节奏是:

   增加测试代码,JUnit出现红条;

   修改产品代码;

   JUnit出现绿条,任务完成;

 

打开Eclipse,单击File->New->Javajava Project  新建一个TDDDemo的项目

在TDDDemo项目中,右键单击TDDDemo,New->Source Folder 新建一个测试目录test

 

右键单击test, New->JUnit Test Case,新建一个测试用例类MyUtilTest

 

增加第一个测试用例testNormal,注意测试用例前一定要有注解@Test。

 

测试结果为红条(red bar),测试有误;

测试结果为绿条(green bar),测试通过。

 

(二)面向对象三要素

(1)抽象

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

复杂系统问题——分层次抽象

抽象最高层,使用问题环境的语言,以概括方式叙述问题的解;

抽象的较低层,采用过程化的方式描述;

抽象包括两个方面:过程抽象  数据抽象

 

 (2)封装、继承与多态

面向对象(Object-Oriented)三要素:封装、继承、多态。

面向对象分析(OOA)根据抽象关键的问题域来分解系统;

面向对象设计(OOD)提供符号设计系统的面向对象的实现过程,通过模型来实现功能规范

面向对象编程实现(OOP)在设计的基础上用编程语言编码。

OOD中建模用图形化的建模语言UML(Unified Modeling Language),Windows中推荐使用StarUML。

过程抽象的结果是函数,数据抽象的结果的抽象数据类型(AbstractData Type,ADT),类可以作具有继承和多态机制的ADT。数据抽象才算OOP的核心和起源。

封装 —— 将数据与相关行为包装在一起以实现信息隐藏。Java中用类进行封装。

封装实际上使用方法(method)将类的数据隐藏起来,控制用户对类的修改和访问数据的 程度,从而带来模块化(Modularity)和信息隐藏(Information hiding)的好处;

接口(interface)是封装的准确描述手段。

 

在实验楼的环境中打开shell,在命令行中输入umbrello,打开UML建模软件umbrello

先单击工具栏上的类图标,再在class diagram(类图)中单击一下,会弹出一个圣诞框,输入类名Dog。

把鼠标放到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

请大家注意UML类图中继承的表示法,是用一个带三角的直线指向父类,通过继承,我们消除了Dog类和Cat类中的重复代码,符合DRY的要求。 继承指一个类的定义可以基于另外一个已经存在的类,即子类基于父类,从而实现父类代码的重用。既存类称作基类、超类、父类(base class、super class、parent class),新类称作派生类、继承类、子类(derived class、inherited class、child class)。继承关系表达了”Is a kind of“的关系,称为“ISA”关系。继承的关键在于确认子类为父类的一个特殊类型 。继承是实现软件可重用的根基,是提高软件系统的可扩展性与可维护性的主要途径。 如上面所示,以封装为基础,继承可以实现代码复用,需要注意的是,继承更重要的作用是实现多态。 面向对象中允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式,我们称此现象为多态性。Java中,多态是指不同的类对象调用同一个签名的成员方法时将执行不同代码的现象。多态是面向对象程序设计的灵活性和可扩展性的基础。 

 

测试代码:

(三)设计模式初步

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

 

 

面向对象三要素是“封装、继承、多态”,任何面向对象编程语言都会在语法上支持这三要素。如何借助抽象思维用好三要素特别是多态还是非常困难的,S.O.L.I.D类设计原则是一个很好的指导:

对扩充开放(Open For Extension )要求软件模块的行为必须是可以扩充的,在应用需求改变或需要满足新的应用需求时,我们要让模块以不同的方式工作; 对修改封闭(Closed for Modification )要求模块的源代码是不可改动的,任何人都不许修改已有模块的源代码。 基于OCP,利用面向对象中的多态性(Polymorphic),更灵活地处理变更拥抱变化,OCP可以用以下手段实现:(1)抽象和继承,(2)面向接口编程。 比如,在一个图形系统中,已经存在三个模块Shape,Square,Circle,如下图所示:

用户现大需要一个Triangle模块是一个合理的要求,由于我们使用了多态,原先的模块不需要改变,只要新增加一个模块Triangle就可以了。

SRP的内容是:There should never be more than one reason for a class to change.

对象提供单一职责的高度封装,对象的改变仅仅依赖于单一职责的改变,它基于软件设计中的高内聚性定义。

LSP的内容:

 

LSP的核心思想是父类型对象可以被子类型对象所取代。

LSP原则清楚地指出,OOD中“ISA关系”是就行为功能而言。行为功能(behavior)不是内在的、私有的,而是外在、公开的,是客户程序所依赖的接口。

ISP的内容:Clients should not be forced to depend upin interfaces that they do not use.

首先要理解客户是什么,前面讲到一个XXXX类,要有一个配套的XXXXTest类,XXXXTest中要使用XXXX类,XXXXTest类就可以看作XXXX类的客户.

接口RowSetManager的功能过多,我们应该把这个接口分成多个接口,以便利于复用.

DIP的内容:

 

 

通过接口或者抽象类,DIP在应用中通过依赖注入的方式实现解耦,重用低级模块,重用实现,解除依赖。 我们看一个例子:设想一个简单的程序,其任务就是实现将键盘输入的字符拷贝到打印机上。进一步假设实现平台中的操作系统并不支持设备无关性。

上层函数copy依赖下层函数read,write。 DIP改变了依赖的方向,要求下面依赖上面,我们使用抽象类实现DIP。

(2)模式与设计模式

 

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

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

(3)设计模式实示例

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

随着系统中对象的数量增多,对象之间的交互成指数增长,设计模式可以帮我们以最好的方式来设计系统。设计模式背后是抽象和SOLID原则。 设计模式有四个基本要素:

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

(四)练习

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

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

步骤

耗时

百分比

需求分析

10min

13.3%

设计

15min

20%

代码实现

20min

26.7%

测试

20min

26.7%

分析总结

10min

13.3%

伪代码:

  1)复数类ComplexNumber的属性
  realPart: 实部,代表复数的实数部分
  imaginPart: 虚部,代表复数的虚数部分
  2)复数类ComplexNumber的方法
  ComplexNumber() 构造函数,将实部,虚部都置为0
  ComplexNumber(double realPart, double imaginPart) 构造函数,创建复数对象的同时完成复数的实部,虚部的初始化
  getRealPart() 获取实部
  getImaginaryPart() 获取虚部
  getRealPart(double realPart) 设置实部
  getImaginaryPart(double imaginPart) 设置虚部
  add(ComplexNumber c) 复数相加
  add(double realPart2) 复数相加
  minus(ComplexNumber c) 复数相减
  minus(double realPart2) 复数相减
  ComplexMulti(ComplexNumber c)  复数相乘
  ComplexMulti(double realPart2)  复数相乘
  toString() 把当前复数对象的实部,虚部组合成a+bi的字符串形式

产品代码为:

测试代码:

 

【实验体会】

通过TDD模式编程,可以从结果出发,对产品代码进行测试,并进行完善,能够使自己的产品代码更加严谨,更加完善。

这次实验中,我不光学习到了很多开发过程中的原则,也体会到自己学习上的不足,需要我在今后的学习生活中多加练习。也学到了一些新的知识,如果发现bug或者新的功能添加,可以马上通过运行单元测试来验证之前完成的代码是否正确。