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

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


实验内容

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

实验步骤

一、 单元测试

  • 三种代码
  1. 伪代码
  2. 产品代码
  3. 测试代码
    伪代码是产品代码最自然、最好的注释。那么什么是伪代码呢?如下图所示:
   如果成绩小于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 "错误";
   }
}

测试代码就是对成品产品代码的验算与检验,证明成品代码没有问题。正对MyUtil我们写一个MyUtilTest.java的测试模块,代码如下:

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

二、 TDD(Test Dirven Development,测试驱动开发)

  • TDD的一般步骤如下:
  1. 明确当前要完成的功能,记录成一个测试列表
  2. 快速完成编写针对此功能的测试用例
  3. 测试代码编译不通过(没产品代码呢)
  4. 编写产品代码
  5. 测试通过
  6. 对代码进行重构,并保证测试通过(重构下次实验联系)
  7. 循环完成所有功能的开发
    基于TDD,我们不会出现过度设计的情况,需求通过测试用例表达出来,我们的产品代码只要让测试通过就可以了。
    就像这样


三、 面向对象三要素

  1. 抽象
    "去粗取精、化繁为简、由表及里、异中求同"。抽象就是抽出事物的本质特征而暂时不考虑他们的细节。对于复杂系统问题人们借助分层次抽象的方法进行问题求解;在抽象的最高层,可以使用问题环境的语言,以概括的方式叙述问题的解。在抽象的较低层,则采用过程化的方式进行描述。在描述问题解时,使用面向问题和面向实现的术语。程序设计中,抽象包括两个方面,一是过程抽象,二是数据抽象。
  2. 封装、继承与多态
    面向对象(Object-Oriented)的三要素包括:封装、继承、多态。过程抽象的结果是函数,数据抽象的结果是抽象数据类型(Abstract Data Type,ADT),类可以作具有继承和多态机制的ADT。数据抽象才是OOP的核心和起源。
  • 构造函数实现对象初始化流程的封装。方法封装了操作对象的流程。Java中还可以用private封装对象私有数据成员。封装的目的主要就是隐藏对象细节,将对象当做黑箱进行操作。实例代码如下:
public class Dog {
    private String color;
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public String bark(){
        return "汪汪";
    }
    public String toString(){
        return "The Dog's color is " + this.getColor() +", and it shouts "+ this.bark() + "!";
    }
}
  • 继承基本上就是为了避免多个类间重复定义行为共同行为。子类利用关键词extends继承父类,避免重复的行为定义。实例代码如下:
public abstract class Animal {
    private String color;
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public abstract String shout(); 
}
public class Dog extends Animal{
    public String shout(){
        return "汪汪";
    }
    public String toString(){
        return "The Dog's color is " + this.getColor() +", and it shouts "+ this.shout() + "!";
    }
}
public class Cat extends Animal{
    public String shout(){
        return "喵喵";
    }
    public String toString(){
        return "The Cat's color is " + this.getColor() +", and it shouts "+ this.shout() + "!";
    }
}
  • 多态就是使用单一接口操作多种类型的对象。实例代码如下:
package cc.openhome;
public class RPG {
    public static void main(String[] args) {
        SwordsMan swordsMan = new SwordsMan();
        swordsMan.setName("Justin");
        swordsMan.setLevel(1);
        swordsMan.setBlood(200);
        Magician magician = new Magician();
        magician.setName("Monica");
        magician.setLevel(1);
        magician.setBlood(100);
        drawFight(swordsMan);
        drawFight(magician);
    }
    static void drawFight(Role role) {
        System.out.print(role.getName());
        role.fight();
    }
}

四、 UML的使用
给一个代码,很难第一时间了解这个代码的整体结构组成,这时候如果利用StarUML软件进行UML建模,
代码的结构组成,各个类的成员变量就可以一目了然了,想这样




前缀减号的代表是Private,前缀加号的代表public


五、 设计模式与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. 设计模式实示例
    设计模式有四个基本要素:
    Pattern name:描述模式,便于交流,存档
    Problem:描述何处应用该模式
    Solution:描述一个设计的组成元素,不针对特例
    Consequence:应用该模式的结果和权衡(trade-offs)
    六、 练习作业(能力有限,代码不是原创,借鉴同学或者百度,具体参见参考资料)
    代码模块介绍
  3. 复数类ComplexNumber的属性
    m_dRealPart: 实部,代表复数的实数部分
    m_dImaginPart: 虚部,代表复数的虚数部分
  4. 复数类ComplexNumber的方法
    ComplexNumber() 构造函数,将实部,虚部都置为0
    ComplexNumber(double r, double I) 构造函数,创建复数对象的同时完成复数的实部,虚部的初始化
    GetRealPart() 获取实部
    GetImaginaryPart() 获取虚部
    SetRealPart(double d) 设置实部
    SetImaginaryPart(double d) 设置虚部
    ComplexAdd(ComplexNumber c) 复数相加
    ComplexAdd(double c) 复数相加
    ComplexMinus(ComplexNumber c) 复数相减
    ComplexMinus(double c) 复数相减
    ComplexMulti(ComplexNumber c)  复数相乘
    ComplexMulti(double c)  复数相乘
    toString() 把当前复数对象的实部,虚部组合成a+bi的字符串形式
    产品代码
    暂无,因为还不会做。
    测试代码
    暂无,因为还不会做。

遇到的问题与解决方式

由于个人未完成实验代码的编写,此模块内容暂时空白,待补。


体会

之前看到老师发的实验内容,内心一片茫然,没有一点点防备也没有一点点头绪。书到用时方恨少还真那么一回事。平时照着课本敲了代码,看着都懂。待到做实验,真枪实弹检验平常所学时,看着闪烁的光符,犹豫再三却还是不敢下笔,因为脑子里一片空白。看其他同学的代码,感觉也能看懂,但让自己写还是不可以,只能靠晦涩生硬的记忆默写刚才借鉴的同学代码。但实验报告总不能交上白卷一张吧,虽然我不会写,但我还是尝试注释一下其他同学优秀博客的代码吧。以下是卢肖明同学的代码:
测试代码:

package Test;
import exp1.MyComplex;
import org.junit.Test;
import static org.junit.Assert.*;
public class MyComplexTest {
    MyComplex a=new MyComplex(1,2);\ 创建一个实例a
    MyComplex b=new MyComplex(1,-4); \ 创建一个实例b
    MyComplex c=new MyComplex(19,0); \ 创建一个实例c
    MyComplex d=new MyComplex(0,-3); \ 创建一个实例d
    MyComplex e=new MyComplex(0,0);  \ 创建一个实例e
    @Test
    public void getRealPart() throws Exception {
        assertEquals(1, MyComplex.getRealPart(1));\ 输入实部数字1,检验得到实部数字是否为1
        assertEquals(-1, MyComplex.getRealPart(-1));\ 输入实部数字-1,检验得到实部数字是否为-1
        assertEquals(5, MyComplex.getRealPart(5));  \ 输入实部数字5,检验得到实部数字是否为5
        assertEquals(22, MyComplex.getRealPart(22));\ 输入实部数字22,检验得到实部数字是否为22
        assertEquals(-100, MyComplex.getRealPart(-100));\ 输入实部数字-100,检验得到实部数字是否为-100
        assertEquals(0, MyComplex.getRealPart(0));\ 输入实部数字0,检验得到实部数字是否为0
    }
    @Test
    public void getImaginePart() throws Exception {
        assertEquals(1, MyComplex.getImaginePart(1));\ 输入虚部数字1,检验得到虚部数字是否为1
        assertEquals(-1, MyComplex.getImaginePart(-1));\ 输入虚部数字-1,检验得到虚部数字是否为-1
        assertEquals(5, MyComplex.getImaginePart(5));\ 输入虚部数字5,检验得到虚部数字是否为5
        assertEquals(22, MyComplex.getImaginePart(22));\ 输入虚部数字22,检验得到虚部数字是否为22
        assertEquals(-100, MyComplex.getImaginePart(-100));\ 输入虚部数字-100,检验得到虚部数字是否为-1
        assertEquals(0, MyComplex.getImaginePart(0));\ 输入虚部数字0,检验得到虚部数字是否为0
    }
    @Test
    public void add() throws Exception {
        assertEquals("(2.0-2.0i)", a.add(b).toString());\计算复数a加b,并检验
        assertEquals("(20.0+2.0i)", a.add(c).toString());\ 计算复数a加c,并检验
        assertEquals("(1.0-1.0i)", a.add(d).toString());\ 计算复数a加d,并检验
        assertEquals("(1.0+2.0i)", a.add(e).toString());\ 计算复数a加e,并检验
    }
    @Test
    public void minus() throws Exception {
        assertEquals("(0.0+6.0i)", a.minus(b).toString());\ 计算复数a减b,并检验
        assertEquals("(-18.0+2.0i)", a.minus(c).toString());\ 计算复数a减b,并检验
        assertEquals("(1.0+5.0i)", a.minus(d).toString());\ 计算复数a减b,并检验
        assertEquals("(1.0+2.0i)", a.minus(e).toString());\ 计算复数a减b,并检验
    }
    @Test
    public void multiply() throws Exception {
        assertEquals("(9.0-2.0i)", a.multiply(b).toString());\ 计算复数a乘b,并检验
        assertEquals("(19.0+38.0i)", a.multiply(c).toString());\ 计算复数a乘c,并检验
        assertEquals("(6.0-3.0i)", a.multiply(d).toString());\ 计算复数a乘d,并检验
        assertEquals("(0.0)", a.multiply(e).toString());\ 计算复数a乘e,并检验
    }
}

产品代码:

package exp1;
import java.util.Scanner;
public class MyComplex {
    static int r;
    static int i;
    private double m;
    private double n;
    public static int getRealPart(int RealPart){   \  输入实部r
        r = RealPart;
        return r;
    }
    public static int getImaginePart(int ImaginePart){  \  输入虚部i
        i = ImaginePart;
        return i;
    }
    public MyComplex(double m, double n) { 
        this.m = m;
        this.n = n;
    }
    public MyComplex add(MyComplex c) {    \ 定义复数加法方法
        return new MyComplex(m + c.m, n + c.n);
    }
    public MyComplex minus(MyComplex c) {    \ 定义复数减法方法
        return new MyComplex(m - c.m, n - c.n);
    }
    public MyComplex multiply(MyComplex c) {    \ 定义复数乘法方法
        return new MyComplex(m * c.m - n * c.n, m * c.n + n * c.m);\ 复数乘法细节,i的平方为-1
    }
    public String toString() {
        String rtr_str = "";
        if (n > 0)
            rtr_str = "(" + m + "+" + n + "i" + ")";
        if (n == 0)
            rtr_str = "(" + m + ")";
        if (n < 0)
            rtr_str = "(" + m + n + "i" + ")";
        return rtr_str;
    }
}

PSP(Personal Software Process)时间

步骤 耗时 百分比
需求分析
设计
代码实现
测试
分析总结

参考资料

*Java学习笔记(第8版)
*《Java学习笔记(第8版)》学习指导
[高其同学博客](http://www.cnblogs.com/20145221GQ/p/5401463.html)
卢肖明同学博客

原文地址:https://www.cnblogs.com/qiwei/p/5402298.html