20145223《Java程序程序设计》实验报告二

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

实验内容

  1. 初步掌握单元测试和TDD
  2. 理解并掌握面向对象三要素:封装、继承、多态
  3. 初步掌握UML建模
  4. 熟悉S.O.L.I.D原则
  5. 了解设计模式

    实验步骤

    (一)单元测试

    (1) 三种代码:当用程序解决问题时,要写三种码:伪代码、产品代码、测试代码。
    需求:我们要在一个MyUtil类中解决一个百分制成绩转成“优、良、中、及格、不及格”五级制成绩的功能。

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

(3)先写伪代码,伪代码与具体编程语言无关,可以使用与编程语法无关的语句(最好英语),有了伪代码后,用编程语言翻译伪代码,就可以得到产品代码,截图如下:
890352-20160417185336129-893856166.png

(4)建一个XXXXTest的类,针对MyUtil类,写一个MyUtilTest.java的测试模块,这里我们设计了一个测试用例(Test Case),测试用例是为某个特殊目标而编制的一组测试输入、执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求。这里我们的测试输入是“50”,预期结果是“不及格”。
890352-20160417185518160-340794596.png

运行结果如下,测试结果符合预期,截图如下:
890352-20160417185532598-433745723.png

(5)只有一组输入的测试是不充分的,我们把一般情况都测试一下,代码如下:
890352-20160417185837051-99966124.png

结果如下:
890352-20160417185851457-1464701458.png

(6)我们不能只测试正常情况,下面看看异常情况如何,比如输入为负分或大于100的成绩,代码如下:
890352-20160417190023660-52382840.png

结果如下:
890352-20160417190041660-390450478.png

·运行程序发现负分时与期望不一致,终于找到了一个bug,原因是判断不及格时没有要求成绩大于零。我们修改MyUtil.java,增加对负分的判断,代码如下:
890352-20160417190254691-1036425812.png

再次运行测试,测试结果符合预期,如下图所示:
890352-20160417190333363-1619949360.png

890352-20160417190346457-1232354556.png

(7)测试边界情况,对输入为“0,60,70,80,90,100”这些边界情况进行测试的代码如下:
890352-20160417190452066-1662218881.png

890352-20160417190500941-55086603.png

·我们发现边界情况中输入100时有一个Bug。我们修改MyUtil.java,把判断优秀的条件中包含输入为100的情况,代码如下:
890352-20160417190644051-1505683979.png

再次执行测试,结果如下:
890352-20160417190710457-1596567679.png

890352-20160417190722004-782607064.png

(二)TDD(Test Driven Devlopment, 测试驱动开发):就是一种先写测试代码,然后再写产品代码的开发方法。

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

890352-20160417194657082-1883283451.png

在MyUtilTest类中运行,绿色表示被执行部分测试通过。

(三)面向对象三要素

(1)抽象
抽象一词的本意是指人在认识思维活动中对事物表象因素的舍弃和对本质因素的抽取。抽象是人类认识复杂事物和现象时经常使用的思维工具,抽象思维能力在程序设计中非常重要,"去粗取精、化繁为简、由表及里、异中求同"的抽象能力很大程度上决定了程序员的程序设计能力。

抽象就是抽出事物的本质特征而暂时不考虑他们的细节。对于复杂系统问题人们借助分层次抽象的方法进行问题求解;在抽象的最高层,可以使用问题环境的语言,以概括的方式叙述问题的解。在抽象的较低层,则采用过程化的方式进行描述。在描述问题解时,使用面向问题和面向实现的术语。

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

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

OOD中建模会用图形化的建模语言UML(Unified Modeling Language),UML是一种通用的建模语言,我们实验中使用umbrello进行建模,Windows中推荐大家使用 StarUML。

过程抽象的结果是函数,数据抽象的结果是抽象数据类型(Abstract Data Type,ADT),类可以作具有继承和多态机制的ADT。数据抽象才是OOP的核心和起源。
OO三要素的第一个要素是封装,封装就是将数据与相关行为包装在一起以实现信息就隐藏。

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

(四)设计模式初步

(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,依赖倒置原则)

1、OCP是OOD中最重要的一个原则,OCP的内容是:

software entities (class, modules, function, etc.) should open for extension,but closed for modification.

软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。

基于OCP,利用面向对象中的多态性(Polymorphic),更灵活地处理变更代码,OCP可以用以下手段实现:(1)抽象和继承,(2)面向接口编程。

2、SRP的内容是:

There should never be more than one reason for a class to change

决不要有一个以上的理由修改一个类

3、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

子类必须可以被其基类所代

使用指向基类的指针或引用的函数,必须能够在不知道具体派生类对象类型的情况下使用它

4、LSP的核心思想是父类型对象可以被子类型对象所取代。前面举的Animal,Dog,Cat的那个例子是符合LSP原则的。LSP主张不要滥用继承,LSP原则清楚地指出,OOD中“ISA关系”是就行为功能而言。行为功能(behavior)不是内在的、私有的,而是外在、公开的,是客户程序所依赖的接口。

5、ISP的内容是:

Clients should not be forced to depend upon interfaces that they do not use

客户不应该依赖他们并未使用的接口

6、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

7、高层模块不应该依赖于低层模块。二者都应该依赖于抽象,抽象不应该依赖于细节。细节应该依赖于抽象 通过接口或者抽象类,DIP在应用中通过依赖注入的方式实现解耦,重用低级模块,重用实现,解除依赖。

(2)模式与设计模式

模式是某外在环境(Context) 下﹐对特定问题(Problem)的惯用解决之道(Solution)。模式必须使得问题明晰,阐明为什么用它来求解问题,以及在什么情况下有用,什么情况下不能起作用,每个模式因其重复性从而可被复用,本身有自己的名字,有可传授性,能移植到不同情景下。模式可以看作对一个问题可复用的专家级解决方法。 计算机科学中有很多模式:GRASP模式、分析模式 、软件体系结构模式 、设计模式:创建型,结构型,行为型、管理模式: The Manager Pool 实现模式、界面设计交互模式 …

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

(3)设计模式实示例:设计模式(design pattern)提供一个用于细化软件系统的子系统或组件,或它们之间的关系图,它描述通信组件的公共再现结构,通信组件可以解决特定语境中的一个设计问题。 随着系统中对象的数量增多,对象之间的交互成指数增长,设计模式可以帮我们以最好的方式来设计系统。设计模式背后是抽象和SOLID原则。 设计模式有四个基本要素:

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

Problem:描述何处应用该模式

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

Consequence:应用该模式的结果和权衡(trade-offs)

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

(1)产品代码:

package com.company;

import java.io.PrintStream;
import java.util.Scanner;
class ComplexNumber {
    private float m_dRealPart;
    private float m_dImaginPart;

     ComplexNumber()
    {
        this(0,0);
    }
    ComplexNumber(float m_dRealPart,float m_dImaginPart){
        this.m_dRealPart=m_dRealPart;
        this.m_dImaginPart=m_dImaginPart;
    }

      public void Add(ComplexNumber p)
    {
        ComplexNumber result=new ComplexNumber();
        result.m_dRealPart=this.m_dRealPart+p.m_dRealPart;
        result.m_dImaginPart=this.m_dImaginPart+p.m_dImaginPart;
        System.out.print("加法结果为:"+result.m_dRealPart+"+"+result.m_dImaginPart+"i");
    }

    public void Sub(ComplexNumber p)
    {
        ComplexNumber result=new ComplexNumber();
        result.m_dRealPart=this.m_dRealPart-p.m_dRealPart;
        result.m_dImaginPart=this.m_dImaginPart-p.m_dImaginPart;
        System.out.print("加法结果为:"+result.m_dRealPart+"+"+result.m_dImaginPart+"i");
    }

    public void Mul(ComplexNumber p)
    {
        ComplexNumber result=new ComplexNumber();
        result.m_dRealPart=this.m_dRealPart*p.m_dRealPart-this.m_dImaginPart*p.m_dImaginPart;
        result.m_dImaginPart=this.m_dRealPart*p.m_dImaginPart+p.m_dRealPart*this.m_dImaginPart;
        System.out.print("乘法结果为:"+result.m_dRealPart+"+"+result.m_dImaginPart+"i");
    }

    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        Object getm_dRealPart;
        ComplexNumber fushu1=new ComplexNumber();
         ComplexNumber fushu2=new ComplexNumber();
        System.out.println("请输入fushu1的实数部分:");//fushu1的实数

        fushu1.m_dRealPart = input.nextInt();
        System.out.println("请输入fushu1的叙述部分:");//fushu1的实数
        fushu1.m_dImaginPart = input.nextInt();
        System.out.println("请输入fushu2的实数部分:");//fushu1的实数
        fushu2.m_dRealPart = input.nextInt();
        System.out.println("请输入fushu1的叙述部分:");//fushu1的实数
        fushu2.m_dImaginPart = input.nextInt();


        fushu1.Add(fushu2);
        fushu1.Sub(fushu2);
        fushu1.Mul(fushu2);

    }
}

(2)运行结果:
890352-20160417195641520-761880613.png

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

步骤耗时百分比
需求分析1h20%
设计1h20%
代码实现120min40%
测试95min15%
分析总结15min5%

实验总结和心得体会

这次的实验重点在于掌握单元测试和TDD、理解并掌握面向对象三要素:封装、继承、多态,同时还要理解一些概念性的问题,并且与实践相结合,比如初步掌握UML建模,熟悉S.O.L.I.D原则,了解设计模式等。其实实验的相关步骤很多在老师的博客里都已经给出,你只需要按照要求一步步操作实现就可以,但是后面的一些概念性的问题就需要你自己去思考。怎么样去编译一个代码,去实现一个问题的解决,去完善代码,都是要靠你去理解这些看似很抽象、但实际上却很是用的东西。

转载于:https://www.cnblogs.com/20145223ymy/p/5393680.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值