20155202 《Java程序设计》实验二(面向对象程序设计)实验报告

20155202 《Java程序设计》实验二(面向对象程序设计)实验报告

代码托管

实验内容

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

实验步骤

(一)单元测试

三种代码
  • 伪代码
  • 产品代码
  • 测试代码
  • 在这里我通过一个复数计算的例子来验证这三种代码。
  • 代码需求
实现复数的加法、减法和乘法计算,并输出结果的功能。
  • 伪代码

先写出程序的伪代码,伪代码是一个程序最好的注释,有助于我们理解问题和解决问题,改程序伪代码如下:

百分制转五分制:
   如果成绩小于60,转成“不及格”
   如果成绩在60与70之间,转成“及格”
   如果成绩在70与80之间,转成“中等”
   如果成绩在80与90之间,转成“良好”
   如果成绩在90与100之间,转成“优秀”
   其他,转成“错误”
产品代码
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 "错误";
    }
}
  • 测试代码:
    以50分测试
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!");
    }
}
  • 测试正常情况
public class MyUtilTest {
    public static void main(String[] args) {
        //测试正常情况
        if(MyUtil.percentage2fivegrade(55) != "不及格")
            System.out.println("test failed!");
        else if(MyUtil.percentage2fivegrade(65) != "及格")
            System.out.println("test failed!");
        else if(MyUtil.percentage2fivegrade(75) != "中等")
            System.out.println("test failed!");
        else if(MyUtil.percentage2fivegrade(85) != "良好")
            System.out.println("test failed!");
        else if(MyUtil.percentage2fivegrade(95) != "优秀")
            System.out.println("test failed!");
        else 
            System.out.println("test passed!");
    }
}

  • 测试异常情况
  • 测试负分
public class MyUtilTest3 {
    public static void main(String[] args) {
        //测试出错情况
        if(MyUtil2.percentage2fivegrade(-10) != "错误")
            System.out.println("test failed 1!");
        else if(MyUtil2.percentage2fivegrade(115) != "错误")
            System.out.println("test failed 2!");
        else
            System.out.println("test passed!");
    }
}

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

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

测试驱动开发”(TDD)。TDD的一般步骤如下:
  • 明确当前要完成的功能,记录成一个测试列表

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

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

  • 编写产品代码

  • 测试通过

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

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

  • 源代码

/**
 * Created by zx on 17-4-21.
 */
public class StringBufferDemo{
    public static void main(String [] args){
        StringBuffer buffer = new StringBuffer(20);
        buffer.append("Java ke ");
        buffer.append("shi yan er ");
        System.out.println(buffer.charAt(0));
        System.out.println(buffer.capacity());
        System.out.println(buffer.indexOf("1"));
        System.out.println("buffer = " + buffer.toString());
        System.out.println(buffer.length());
    }

}
  • 进行Junit测试后结果:

  • 右键到类,找其中go to,然后选test

  • 再建立一个新directory,将test放进去

  • 先一步步到添加新的test后,进行测试:

import org.junit.Test;
import junit.framework.TestCase;
/**
 * Created by zx on 17-4-21.
 */
public class StringBufferDemoTest extends TestCase {
    @Test
    public  void test(){
        StringBuffer buffer = new StringBuffer(20);
        buffer.append("Java ke ");
        buffer.append("shi yan er ");
        System.out.println(buffer.charAt(0));
        System.out.println(buffer.capacity());
        System.out.println(buffer.indexOf("1"));
        System.out.println("buffer = " + buffer.toString());
        System.out.println(buffer.length());
    }
    public void test1() {
        StringBuffer buffer = new StringBuffer(20);
        buffer.append("Java ke ");
        buffer.append("shi yan er ");
        assertEquals('J', buffer.charAt(0));
    }
}

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

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

- 对设计模式示例进行扩充,体会OCP原则和DIP原则的应用,初步理解设计模式

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

  • 修改后的产品代码如下:

(三)设计模式初步

(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.

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

  • 对扩充开放(Open For Extension)要求软件模块的行为必须是可以扩充的,在应用需求改变或需要满足新的应用需求时,我们要让模块以不同的方式工作; 对修改封闭(Closed for Modification )要求模块的源代码是不可改动的,任何人都不许修改已有模块的源代码。

  • 基于OCP,利用面向对象中的多态性(Polymorphic),更灵活地处理变更拥抱变化,OCP可以用以下手段实现:

  • (1)抽象和继承,

  • (2)面向接口编程。

以TDD的方式开发一个复数类Complex
  • 产品代码如下:
/**
 * Created by zx on 17-4-21.
 */
public class MyComplex {

    //定义属性并生成getter,setter
    double RealPart;
    double ImagePart;

    public double getRealPart() {
        return RealPart;
    }

    public void setRealPart(double realPart) {
        RealPart = realPart;
    }

    public double getImagePart() {
        return ImagePart;
    }

    public void setImagePart(double imagePart) {
        ImagePart = imagePart;
    }

    //定义构造函数
    public MyComplex(double R, double I) {
        RealPart = R;
        ImagePart = I;
    }

    //Override Object
    public boolean equals(MyComplex obj1) {
        if (this.getRealPart () == obj1.getRealPart () && this.getImagePart () == obj1.getImagePart ())
            return true;
        else return false;
    }

    //定义公有方法:加减乘除
    public String toString() {
        return RealPart + " + " + ImagePart + "i";
    }

    public MyComplex ComplexAdd(MyComplex obj) {
        return new MyComplex ( RealPart + obj.getRealPart (), ImagePart + obj.getImagePart () );
    }

    public MyComplex ComplexSub(MyComplex obj) {
        return new MyComplex ( RealPart - obj.getRealPart (), ImagePart - obj.getImagePart () );
    }

    public MyComplex ComplexMulti(MyComplex obj) {
        return new MyComplex ( RealPart * obj.getRealPart () - ImagePart * obj.getImagePart (), RealPart * obj.getImagePart () + ImagePart * obj.getRealPart () );
    }

}
  • 测试代码如下:
/**
 * Created by zx on 17-4-21.
 */
import junit.framework.TestCase;
import org.junit.Test;

public class ComplexTest extends TestCase{
    Complex c1 = new Complex(0, 3);
    Complex c2 = new Complex(-1, -1);
    Complex c3 = new Complex(2,1);
    @Test
    public void testgetRealPart() throws Exception {
        assertEquals(-1.0, Complex.getRealPart(-1.0));
        assertEquals(5.0, Complex.getRealPart(5.0));
        assertEquals(0.0, Complex.getRealPart(0.0));
    }
    @Test
    public void testgetImagePart() throws Exception {
        assertEquals(-1.0, Complex.getImagePart(-1.0));
        assertEquals(5.0, Complex.getImagePart(5.0));
        assertEquals(0.0, Complex.getImagePart(0.0));
    }
    @Test
    public void testComplexAdd() throws Exception {
        assertEquals("-1.0+2.0i", c1.ComplexAdd(c2).toString());
        assertEquals("2.0+4.0i", c1.ComplexAdd(c3).toString());
        assertEquals("1.0", c2.ComplexAdd(c3).toString());
    }
    @Test
    public void testComplexSub() throws Exception {
        assertEquals("1.0+4.0i", c1.ComplexSub(c2).toString());
        assertEquals("-2.0+2.0i", c1.ComplexSub(c3).toString());
        assertEquals("-3.0 -2.0i", c2.ComplexSub(c3).toString());
    }
    @Test
    public void testComplexMulti() throws Exception {
        assertEquals("3.0 -3.0i", c1.ComplexMulti(c2).toString());
        assertEquals("-3.0+6.0i", c1.ComplexMulti(c3).toString());
        assertEquals("-1.0 -3.0i", c2.ComplexMulti(c3).toString());
    }
    @Test
    public void testComplexComplexDiv() throws Exception {
        assertEquals("-1.5 -1.5i", c1.ComplexDiv(c2).toString());
        assertEquals("1.2+0.6i", c1.ComplexDiv(c3).toString());
        assertEquals("-0.6 -0.6i", c2.ComplexDiv(c3).toString());
    }
}

结果截图

用StarUML软件进行UML

  • 选择建立Default Approach工程

  • 选择Design Model

  • 选择Design Model 新建class

  • 添加Attribute

  • 进行封装

  • 添加Operation

  • 添加测试类并建立关系

(四)总结:

设计模式初步

(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,依赖倒置原则)
  • 因为S.O.L.I.D原则在之前的学习中已经阐述了就不在赘述了。
(2)模式与设计模式
  • 模式是某外在环境(Context) 下﹐对特定问题(Problem)的惯用解决之道(Solution)。模式必须使得问题明晰,阐明为什么用它来求解问题,以及在什么情况下有用,什么情况下不能起作用,每个模式因其重复性从而可被复用,本身有自己的名字,有可传授性,能移植到不同情景下。模式可以看作对一个问题可复用的专家级解决方法。计算机科学中有很多模式:
  • GRASP模式
  • 分析模式
  • 软件体系结构模式
  • 设计模式:创建型,结构型,行为型
  • 管理模式: The Manager Pool 实现模式
  • 界面设计交互模式
  • 这里面最重要的是设计模式,在面向对象中设计模式的地位可以和面向过程编程中的数据结构的地位相当。
(3)设计模式实示例

设计模式可以帮我们以最好的方式来设计系统。设计模式背后是抽象和SOLID原则。
设计模式有四个基本要素:

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

实验体会及总结

  • 这次实验比较多,而且新东西挺多,比较麻烦,所以遇到一些问题,和同学进行深入讨论,结果比较满意。
    
  • 在本次实验中,我学会了测试代码,虽然对于代码进行测试看上去很麻烦,感觉多此一举,但是就像老师所说的,代码中的bug越早越容易发现,如果拖到代码庞大之后再去编写测试的话,工作量太大了,而且一旦出现什么错误,找到BUG是一件十分困难的事情。
    
  • 通过单元测试,我们就可以很大程度的避免那些错误,也许现在的效果不明显,但是如果我们养成这个习惯,在以后编写大量的代码程序的时候就可以帮助我们队代码查缺补漏,大大的节约我们的时间和成本。
    
步骤 耗时 百分比
需求分析 15分钟 9%
设计 15分钟 9%
代码实现 110分钟 64%
测试 10分钟 6%
分析总结 20分钟 12%
posted @ 2017-04-21 22:20  20155202张旭  阅读(870)  评论(0编辑  收藏  举报