20145317彭垚《Java程序设计》实验二
20145317《Java程序设计》实验二Java面向对象程序设计实验报告
实验内容
- 初步掌握单元测试和TDD
- 理解并掌握面向对象三要素:封装、继承、多态
- 初步掌握UML建模
- 熟悉S.O.L.I.D原则
- 了解设计模式
实验过程
(一)单元测试
(1) 三种代码:
伪代码、产品代码、测试代码。
(2)TDD(Test Driven Devlopment, 测试驱动开发)
定义:这种先写测试代码,然后再写产品代码的开发方法叫“测试驱动开发”(TDD)。一般步骤如下:
①明确当前要完成的功能,记录成一个测试列表
②快速完成编写针对此功能的测试用例
③测试代码编译不通过
④编写产品代码
⑤测试通过
⑥对代码进行重构,并保证测试通过(重构下次实验练习)
⑦循环完成所有功能的开发
(二)面向对象三要素
(1)抽象
抽象能力是指"去粗取精、化繁为简、由表及里、异中求同"的能力。
程序设计中,抽象包括两个方面,一是过程抽象,二是数据抽象。
(2)封装、继承与多态
面向对象(Object-Oriented)的三要素包括:封装、继承、多态。包括面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程实现(OOP)。
1、封装
定义:将数据与相关行为包装在一起以实现信息就隐藏。
封装实际上使用方法(method)将类的数据隐藏起来,控制用户对类的修改和访问数据的程度,从而带来模块化(Modularity)和信息隐藏(Information hiding)的好处;接口(interface)是封装的准确描述手段。 Dog类通过使用类和访问控制(private,public)隐藏了属性color,开放了接口setColor(),getColor(),bark()和toString。
2、我们可以看到,在UML里,一个类的属性能显示它的名字,类型,初始化值,属性也可以显示private,public,protected。 类的方法能显示它们的方法名,参数,返回类型,以及方法的private,public,protected属性。其中:
①+表示public
②#表示 protected
③-表示 private
3、继承指一个类的定义可以基于另外一个已经存在的类,即子类基于父类,从而实现父类代码的重用。既存类称作基类、超类、父类(base class、super class、parent class),新类称作派生类、继承类、子类(derived class、inherited class、child class)。继承关系表达了”Is a kind of“的关系,称为“ISA”关系。继承的关键在于确认子类为父类的一个特殊类型。它有以下等特点:
①继承是实现软件可重用的根基,是提高软件系统的可扩展性与可维护性的主要途径。
②以封装为基础,继承可以实现代码复用,需要注意的是,继承更重要的作用是实现多态。
多态性:面向对象中允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式。在Java中,多态是指不同的类对象调用同一个签名的成员方法时将执行不同代码的现象。多态是面向对象程序设计的灵活性和可扩展性的基础。
(三)设计模式初步
(1)S.O.L.I.D原则
1、面向对象三要素是 “封装、继承、多态”,任何面向对象编程语言都会在语法上支持这三要素。如何借助抽象思维用好三要素特别是多态还是非常困难的,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,依赖倒置原则)
2、OCP是OOD中最重要的一个原则,OCP的内容是:
①software entities (class, modules, function, etc.) should open for extension,but closed for modification。
②软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。对扩充开放,要求软件模块的行为必须是可以扩充的,在应用需求改变或需要满足新的应用需求时,我们要让模块以不同的方式工作;对修改封闭,要求模块的源代码是不可改动的,任何人都不许修改已有模块的源代码。
OCP的实现手段:(1)抽象和继承,(2)面向接口编程。
3、LSP(Liskov Substitusion Principle,Liskov替换原则)
内容:子类必须可以被其基类所代;使用指向基类的指针或引用的函数,必须能够在不知道具体派生类对象类型的情况下使用它。
核心思想是父类型对象可以被子类型对象所取代。
4、ISP(Interface Segregation Principle,接口分离原则)
内容:客户不应该依赖他们并未使用的接口(接口的功能不要太多)
5、DIP(Dependency Inversion Principle,依赖倒置原则)
内容:高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象 。
6、除SOLID原则外还有很多其它的面向对象原则。如:
①"组合替代继承":这是说相对于继承,要更倾向于使用组合;
②"笛米特法则":这是说"你的类对其它类知道的越少越好";
③"共同封闭原则":这是说"相关类应该打包在一起";
④"稳定抽象原则":这是说"类越稳定,越应该由抽象类组成";
(2)模式与设计模式
模式是某外在环境下﹐对特定问题的惯用解决方法,可以看作对一个问题可复用的专家级解决方法。
(3)设计模式实示例
1、四个基本要素:
模式名:描述模式,便于交流,存档
问题:描述何处应用该模式
解决:描述一个设计的组成元素,不针对特例
结果:应用该模式的结果和权衡
2、抽象工厂模式
容易出现过度设计的问题
只是对于整形数据来说的
(四)练习
1使用TDD的方式设计关实现复数类Complex。
伪代码:
①复数的实部为0,虚部为0
②复数的实部为0,虚部为对应数
③复数的实部为对应数,实部为0
④两参数分别为实部与虚部
⑤实现两复数的加法、减法、乘法。
代码:
package czy;
public class complex
{
public static void main (String args[])
{
ComplexNumber a = new ComplexNumber(20,10);
ComplexNumber b = new ComplexNumber(5,5);
double z = 10.0;
System.out.println(a.toString() + " + " +b.toString() + " = " + a.complexAdd(b).toString());
System.out.println(a.toString() + " + " + z + " = " + a.complexAdd(z).toString());
System.out.println(a.toString() + " - " + b.toString() + " = " + a.complexMinus(b).toString());
System.out.println(a.toString() + " - " + z + " = " + a.complexMinus(z).toString());
System.out.println(a.toString() + " * " + b.toString() + " = " + a.complexMinus(b).toString());
System.out.println(a.toString() + " * " + z + " = " + a.complexMulti(z).toString());
}
}
class ComplexNumber
{
private double realpart;
private double imaginpart;
ComplexNumber()
{
realpart = 0.0;
imaginpart = 0.0;
}
ComplexNumber(double r,double I)
{
realpart = r;
imaginpart = I;
}
ComplexNumber(ComplexNumber c)
{
realpart = c.getRealPart();
imaginpart = c.getImaginaryPart();
}
double getRealPart()
{
return realpart;
}
double getImaginaryPart()
{
return imaginpart;
}
void setRealPart(double d)
{
realpart = d;
}
void setImaginaryPart(double d)
{
imaginpart = d;
}
ComplexNumber complexAdd(ComplexNumber c)
{
return new ComplexNumber( this.realpart + c.getRealPart(), this.imaginpart + c.getImaginaryPart());
}
ComplexNumber complexAdd(double c)
{
return new ComplexNumber( this.realpart + c, this.imaginpart);
}
ComplexNumber complexMinus(ComplexNumber c)
{
return new ComplexNumber( this.realpart - c.getRealPart(),this.imaginpart - c.getImaginaryPart());
}
ComplexNumber complexMinus(double c)
{
return new ComplexNumber( this.realpart - c, this.imaginpart);
}
ComplexNumber complexMulti(ComplexNumber c)
{
return new ComplexNumber( this.realpart * c.getRealPart() - this.realpart * c.getImaginaryPart(),
this.realpart * c.getImaginaryPart() + this.imaginpart * c.getRealPart());
}
ComplexNumber complexMulti(double c)
{
return new ComplexNumber( this.realpart * c, this.imaginpart * c);
}
public String toString()
{
return "(" + realpart + " + " + imaginpart + " i" + ")";
}
}
完成后进行测试代码:package czy;
public class complextest {
public static void main (String args[])
{
ComplexNumber a = new ComplexNumber(20,10);
ComplexNumber b = new ComplexNumber(5,5);
ComplexNumber x = new ComplexNumber();
double z = 10.0;
x = a.complexAdd(b);
if(x.getRealPart() !=25.0 )
System.out.println("test failed 1!");
x = a.complexAdd(z);
if(x.getRealPart() !=30.0 )
System.out.println("test failed 11!");
x = a.complexAdd(b);
if( x.getImaginaryPart()!=15.0 )
System.out.println("test failed 2!");
x = a.complexAdd(z);
if( x.getImaginaryPart()!=10.0 )
System.out.println("test failed 22!");
x = a.complexMinus(b);
if( x.getRealPart()!=15.0 )
System.out.println("test failed 3!");
x = a.complexMinus(z);
if( x.getRealPart()!=10.0 )
System.out.println("test failed 33!");
x = a.complexMinus(b);
if( x.getImaginaryPart()!=5.0 )
System.out.println("test failed 4!");
x = a.complexMinus(z);
if( x.getImaginaryPart()!=10.0 )
System.out.println("test failed 44!");
x = a.complexMulti(b);
if( x.getRealPart() !=50.0 )
System.out.println(x.getRealPart()+"test failed 5!");
x = a.complexMulti(z);
if( x.getRealPart() !=200.0 )
System.out.println("test failed 55!");
x = a.complexMulti(b);
if( x.getImaginaryPart()!=150.0 )
System.out.println("test failed 6!");
x = a.complexMulti(z);
if( x.getImaginaryPart()!=100.0 )
System.out.println("test failed 66!");
else
System.out.println("test passed!");
}
}
class ComplexNumber
{
private double realpart;
private double imaginpart;
ComplexNumber()
{
realpart = 0.0;
imaginpart = 0.0;
}
ComplexNumber(double r,double I)
{
realpart = r;
imaginpart = I;
}
ComplexNumber(ComplexNumber c)
{
realpart = c.getRealPart();
imaginpart = c.getImaginaryPart();
}
double getRealPart()
{
return realpart;
}
double getImaginaryPart()
{
return imaginpart;
}
void setRealPart(double d)
{
realpart = d;
}
void setImaginaryPart(double d)
{
imaginpart = d;
}
ComplexNumber complexAdd(ComplexNumber c)
{
return new ComplexNumber( this.realpart + c.getRealPart(), this.imaginpart + c.getImaginaryPart());
}
ComplexNumber complexAdd(double c)
{
return new ComplexNumber( this.realpart + c, this.imaginpart);
}
ComplexNumber complexMinus(ComplexNumber c)
{
return new ComplexNumber( this.realpart - c.getRealPart(),this.imaginpart - c.getImaginaryPart());
}
ComplexNumber complexMinus(double c)
{
return new ComplexNumber( this.realpart - c, this.imaginpart);
}
ComplexNumber complexMulti(ComplexNumber c)
{
return new ComplexNumber( this.realpart * c.getRealPart() - this.realpart * c.getImaginaryPart(),
this.realpart * c.getImaginaryPart() + this.imaginpart * c.getRealPart());
}
ComplexNumber complexMulti(double c)
{
return new ComplexNumber( this.realpart * c, this.imaginpart * c );
}
public String toString()
{
return "(" + realpart + " + " + imaginpart + " i" + ")";
}
}
PSP时间
步骤 | 耗时 | 百分比 |
---|---|---|
需求分析 | 1h | 12.5% |
设计 | 1h | 12.5% |
代码实现 | 3h | 37.5% |
测试 | 1h | 12.5% |
分析总结 | 2h | 25.0% |