• 2017-2018-2 20165315 实验二《Java面向对象程序设计》实验报告


    2017-2018-2 20165315 实验二《Java面向对象程序设计》实验报告

    一、实验内容及步骤

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

    • 单元测试

    任务一:三种代码

    用程序解决问题时,要学会写以下三种代码:

    • 伪代码
    • 产品代码
    • 测试代码

    TDD(测试驱动开发):

    • 伪代码(思路)
    • 测试代码(产品预期功能)
    • 产品代码(实现预期功能)

    TDD的一般步骤如下:

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

    基于TDD,可以有效避免过度开发的现象,因为我们只需要让测试通过即可。

    测试类的创建:

    • 将鼠标放在主类类名上,点击出现的黄色小灯泡,在下拉选项中选择Create Test

    • 随后在Testing Library中的下拉选项中选择Junit 3,点击OK便可创建Test
    • 注意:不要忘记@Test的书写

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

    测试结果:

    任务二:TDD(Test Driven Devlopment, 测试驱动开发)

    老师在教程里给出的程序如下:

    public static void main(String [] args){
           StringBuffer buffer = new StringBuffer();
           buffer.append('S');
           buffer.append("tringBuffer");
           System.out.println(buffer.charAt(1));
           System.out.println(buffer.capacity());
           System.out.println(buffer.length());
           System.out.println(buffer.indexOf("tring"));
           System.out.println("buffer = " + buffer.toString());
    

    对于这个程序,有五个方法需要测试,分别是:

    • charAt(int i):返回此序列中指定索引处的 char 值。第一个 char 值在索引 0 处,第二个在索引 1 处,依此类推,这类似于数组索引
    • capacity():返回当前容量。容量指可用于最新插入的字符的存储量,超过这一容量就需要再次进行分配
    • length():返回子浮窗的长度
    • indexOf(String s):返回输入的子字符串的第一个字母在母字符串的位置
    • toString(String s):返回此对象本身(它已经是一个字符串)

    在产品代码里,我们需要为这五个方法加上返回值,并与我们的断言进行比较。产品代码如下:

    public class StringBufferDemo {
        StringBuffer buffer;
    
        public static char CharAt(StringBuffer buffer, int index) {
            return buffer.charAt(index);
        }
    
        public static int Capacity(StringBuffer buffer) {
            return buffer.capacity();
        }
    
        public static int IndexOf(StringBuffer buffer, String str) {
            return buffer.indexOf(str);
        }
    
        public static String ToString(StringBuffer buffer) {
            return "buffer = " + buffer.toString();
        }
    
        public static int Length(StringBuffer buffer) {
            return buffer.length();
        }
    }
    

    根据上述该产品代码,写出对应的测试类,在测试类中我分别都使使用了3个例子来进行测试,如果出现问题,JUnit会出现红条,IDEA会提示哪一个测试用例出现问题,由此可以对应改正产品代码中的问题,直到JUnit出现绿条,任务完成。

    测试代码如下:

    import junit.framework.TestCase;
    import org.junit.*;
    
    public class StringBufferDemoTest extends TestCase {
        StringBuffer buffer1 = new StringBuffer("iamastudent");
        StringBuffer buffer2 = new StringBuffer("youareastudent");
        StringBuffer buffer3 = new StringBuffer("heisateacher");
    
        @Test
        public void testCharAt() {
            assertEquals('i', StringBufferDemo.CharAt(buffer1, 0));
            assertEquals('o', StringBufferDemo.CharAt(buffer2, 1));
            assertEquals('r', StringBufferDemo.CharAt(buffer3, 11));
        }
    
        @Test
        public void testCapital() {
            assertEquals(27, StringBufferDemo.Capacity(buffer1));
            assertEquals(30, StringBufferDemo.Capacity(buffer2));
            assertEquals(28, StringBufferDemo.Capacity(buffer3));
        }
    
        @Test
        public void testLenght() throws Exception {
            assertEquals(11, StringBufferDemo.Length(buffer1));
            assertEquals(14, StringBufferDemo.Length(buffer2));
            assertEquals(12, StringBufferDemo.Length(buffer3));
        }
    
        @Test
        public void testIndexOf() {
            assertEquals(0, StringBufferDemo.IndexOf(buffer1, "iam"));
            assertEquals(-1, StringBufferDemo.IndexOf(buffer2, "You"));
            assertEquals(11, StringBufferDemo.IndexOf(buffer3, "r"));
        }
    
        @Test
        public void testToString() {
            assertEquals("buffer = iamastudent", StringBufferDemo.ToString(buffer1));
            assertEquals("buffer = youareastudent", StringBufferDemo.ToString(buffer2));
            assertEquals("buffer = heisateacher", StringBufferDemo.ToString(buffer3));
        }
    }
    

    截图如下:

    2.面向对象三要素:封装、继承、多态

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

    OOD中建模会用图形化的建模语言UML(Unified Modeling Language),UML是一种通用的建模语言。

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

    OO三要素的第一个要素是封装,封装就是将数据与相关行为包装在一起以实现信息就隐藏,Java中用类进行封装。

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

    任务三:使用StarUML对实验二中的代码进行建模

    • 点开StarUML程序后,在右下角的选项中选择Class选项建立一个类的UML图

    • 按照下图中提示分别创建对应的变量层和方法层

    • 在右下角选项中选择Generalization选项,创建类与类之间的调用关系

    最终结果截图:

    • 较为简单:

    • 较为复杂:

    3.设计模式

    面向对象三要素是“封装、继承、多态”,任何面向对象编程语言都会在语法上支持这三要素。如何借助抽象思维用好三要素特别是多态还是非常困难的,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,依赖倒置原则)

    任务四:对MyDoc类进行扩充,让其支持Long类,初步理解设计模式

    OCP是OOD中最重要的一个原则,OCP的内容是:
    软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。

    OCP可以用以下手段实现:

    • 抽象和继承
    • 面向接口编程

    老师给出的以Int型为例的代码如下:

    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();      
        }   
    }
    

    在上述代码的基础上,要求系统支持Long类,这是一个合理的要求,要支持Long类,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);
        }
    }
    
    class Long extends Data {
        long value;
    
        Long() {
            value = 111111111;
        }
    
        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();
        }
    }
    
    class LongFactory extends Factory {
        public Data CreateDataObject() {
            return new Long();
        }
    }
    
    //Client classes
    class Document {
        Data pd;
    
        Document(Factory pf) {
            pd = pf.CreateDataObject();
        }
    
        public void DisplayData() {
            pd.DisplayValue();
        }
    }
    
    //Test class
    public class MyDoc {
        static Document d1, d2;
    
        public static void main(String[] args) {
            d1 = new Document(new IntFactory());
            d2 = new Document(new LongFactory());
            d1.DisplayData();
            d2.DisplayData();
        }
    }
    

    运行结果截图:

    4.练习

    任务五:以TDD的方式开发一个复数类Complex

    • 伪代码
    // 定义属性并生成getter,setter
    double RealPart;
    double ImagePart;
    // 定义构造函数
    public Complex()
    public Complex(double R,double I)
    
    //Override Object
    public boolean equals(Object obj)
    public String toString()
    
    // 定义公有方法:加减乘除
    Complex ComplexAdd(Complex a)
    Complex ComplexSub(Complex a)
    Complex ComplexMulti(Complex a)
    Complex ComplexDiv(Complex a)
    
    • 产品代码
    public class Complex {
        // 定义属性并生成getter,setter
        private double RealPart;
        private double ImagePart;
    
        public double getterRealPart() {
            return this.RealPart;
        }
    
        public double getterImagePart() {
            return this.ImagePart;
        }
    
        public static double getterRealPart(double RealPart) {
            return RealPart;
        }
    
        public static double getterImagePart(double ImagePart) {
            return ImagePart;
        }
    
        public void setterRealPart(double RealPart) {
            this.RealPart = RealPart;
        }
    
        public void setterImagePart(double ImagePart) {
            this.ImagePart = ImagePart;
        }
    
        // 定义构造函数
        public Complex() {
            this.RealPart = 0;
            this.ImagePart = 0;
        }
    
        public Complex(double R, double I) {
            this.RealPart = R;
            this.ImagePart = I;
        }
    
        //Override Object
        public boolean equals(Object obj) {
            Complex complex = (Complex) obj;
            if (this == obj) {
                return true;
            } else if (!(obj instanceof Complex)) {
                return false;
            } else if (getterRealPart() != complex.getterRealPart()) {
                return false;
            } else if (getterImagePart() != complex.getterImagePart()) {
                return false;
            } else
                return true;
        }
    
        public String toString() {
            if (getterRealPart() == 0)
                return getterImagePart() + "i";
            else if (getterImagePart() == 0)
                return getterRealPart() + "";
            else if (getterImagePart() < 0)
                return getterRealPart() + "" + getterImagePart() + "i";
            else
                return getterRealPart() + "+" + getterImagePart() + "i";
        }
    
        // 定义公有方法:加减乘除
        Complex ComplexAdd(Complex a) {
            return new Complex(this.getterRealPart() + a.getterRealPart(), getterImagePart() + a.getterImagePart());
        }
    
        Complex ComplexSub(Complex a) {
            return new Complex(this.getterRealPart() - a.getterRealPart(), getterImagePart() - a.getterImagePart());
        }
    
        Complex ComplexMulti(Complex a) {
            return new Complex(this.getterRealPart() * a.getterRealPart() - a.getterImagePart() * this.getterImagePart(), a.getterImagePart() * this.getterRealPart() + a.getterRealPart() * this.getterImagePart());
        }
    
        Complex ComplexDiv(Complex a) {
            Complex c = new Complex();
            if (a.equals(c)) {
                System.out.println("错误,分母不能为零!");
            }
            return new Complex(this.getterRealPart() / a.getterRealPart(), this.getterImagePart() / a.getterImagePart());
        }
    }
    
    • 测试代码
    import junit.framework.TestCase;
    import org.junit.Test;
    
    public class ComplexTest extends TestCase {
        Complex complex1 = new Complex(3, 4);
        Complex complex2 = new Complex(1, -2);
        Complex complex3 = new Complex(1, 1);
    
        @Test
        public void testgetterRealPart() throws Exception {
            assertEquals(3.0, Complex.getterRealPart(3.0));
            assertEquals(1.0, Complex.getterRealPart(1.0));
            assertEquals(-2.0, Complex.getterRealPart(-2.0));
        }
    
        @Test
        public void testgetterImagePart() throws Exception {
            assertEquals(4.0, Complex.getterImagePart(4.0));
            assertEquals(-2.0, Complex.getterImagePart(-2.0));
            assertEquals(0.0, Complex.getterImagePart(0.0));
        }
    
        @Test
        public void testAdd() throws Exception {
            assertEquals("4.0+2.0i", complex1.ComplexAdd(complex2).toString());
            assertEquals("4.0+5.0i", complex1.ComplexAdd(complex3).toString());
            assertEquals("2.0-1.0i", complex2.ComplexAdd(complex3).toString());
        }
    
        @Test
        public void testSub() throws Exception {
            assertEquals("2.0+6.0i", complex1.ComplexSub(complex2).toString());
            assertEquals("2.0+3.0i", complex1.ComplexSub(complex3).toString());
            assertEquals("-3.0i", complex2.ComplexSub(complex3).toString());
        }
    
        @Test
        public void testMulti() throws Exception {
            assertEquals("11.0-2.0i", complex1.ComplexMulti(complex2).toString());
            assertEquals("-1.0+7.0i", complex1.ComplexMulti(complex3).toString());
            assertEquals("3.0-1.0i", complex2.ComplexMulti(complex3).toString());
        }
    
        @Test
        public void testDiv() throws Exception {
            assertEquals("3.0-2.0i", complex1.ComplexDiv(complex2).toString());
            assertEquals("3.0+4.0i", complex1.ComplexDiv(complex3).toString());
            assertEquals("1.0-2.0i", complex2.ComplexDiv(complex3).toString());
        }
    }
    

    二、实验过程中遇到的问题及解决

    • 一开始以为Test类是自己手动在Test目录下创建,导致结果不会出现测试条

    解决过程:

    1.将鼠标放在主类类名上,点击出现的黄色小灯泡,在下拉选项中选择Create Test

    2.随后在Testing Library中的下拉选项中选择Junit 3,点击OK便可创建Test

    3.注意:不要忘记@Test的书写

    • 在书写测试类时,StringBuffer()中的capacity()方法总是出错

    解决方法:
    参考capacity()的用法可知,StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候, StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容 量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值+2)。

    也就是说,对于空字符串,调用capacity()方法初始分配值为16;大小超过16时则扩充容量为34,再次扩充得到70。

    三、实验体会与总结

    我通过本次实验学会了如何编写测试代码、如何绘画UML图以及在TDD模式下编写代码。

    在自己上手实践操作过程中,加深了对平时不清楚的知识点的理解,也掌握了的Junit的用法。在使用测试代码的时候,既可以测试到代码是否正确,又规范了编程习惯,单元测试提供了一种高效快速的测试代码正确性的方法。

    四、PSP(Personal Software Process)时间

    步骤 耗时 百分比
    步骤 耗时 百分比
    需求分析 20min 12%
    设计 30min 18%
    代码实现 50min 30%
    测试 30min 18%
    分析总结 40min 22%

    五、代码链接

    https://gitee.com/BESTI-IS-JAVA-2018/ch1/tree/master/20165315exp2

  • 相关阅读:
    最长公共子序列
    小测试 炒书
    洛谷P1968 美元汇率
    洛谷P3611 [USACO17JAN]Cow Dance Show奶牛舞蹈
    【刷题】【树】【模拟】树网的核
    【刷题】【dp】地精的贸易
    【刷题】【模拟】复制cs
    【刷题】【模拟】3n+1
    【刷题】【dp】中国象棋
    【刷题】【搜索】新数独
  • 原文地址:https://www.cnblogs.com/yh666/p/8892416.html
Copyright © 2020-2023  润新知