面向对象思考

  本章主要讲述问题求解和基本程序设计的技术,以体会面向过程和面向对象程序设计的不同之处。我们的焦点放在类的设计上,通过几个例子来诠释面向对象方法的优点,这些例子包括如何在应用程序中设计新类、如何使用这些类。通过这些案例的学习来学会如何高效的使用面向程序设计。

类的抽象和封装

  类抽象(class abstraction)是将类的实现和使用分离。类的创建者描述类的功能,让使用者明白如何才能使用类。从类外可以访问的方法和数据域的集合以及预期这些成员如何行为的描述称为类的合约。

如图所示,类的使用者不需要知道类是如何实现的。实现的细节经过封装对用户隐藏起来,称为类的封装。

  类的抽象和封装是一个问题的两个方面。现实生活中很多例子可以说明类抽象的概念。例如:考虑建立一个计算机系统。计算机有很多组件 —— CPU、内存、磁盘、主板和风扇等。每个组件都可以看作是一个由属性和方法的对象。要使各个组件一起工作,只需要知道每个组件是如何用的以及是如何与其它组件进行交互的,而无须了解这些组件内部是如何工作的。内部功能的实现被封装起来,对使用者是隐藏的。所以,你可以组装一台计算机,而不需要了解每个组件是如何实现的。

  对计算机系统的模拟准确地反映了面向对象方法。每个组件可以看成组件类的对象。

  再用我们作业中的一笔贷款作为另一个例子。一笔贷款可以看作贷款类Loan的一个对象,利率,贷款额以及还贷周期都是它的数据属性,计算每月偿还额和总偿还额是它的行为(方法)。当你购买一个程序员鼓励师的时候,就用贷款利率、贷款额和还贷周期实例化这个类,创建一个贷款对象。然后,就可以用这些方法计算贷款的月偿还额和总偿还额(再然后就是和鼓励师过上“幸福”的生活)。作为一个贷款类Loan的用户,是不需要知道这些方法是如何实现的。

  假设希望将一个日期和这个贷款联系起来。传统的面向过程编程时动作驱动的,数据和动作时分离的。面向对象编程的范式重点在于对象,动作和数据一起定义在对象中。为了将日期和贷款联系起来,可以定义一个贷款类,将日期和贷款的其它属性作为数据域,并且贷款数据和动作在一个对象中完成。

   将上面的UML图看着Loan的合约,下面我们扮演Loan类的开发者

package com.iweb.demo;

import java.time.LocalDate;

/**
 * 贷款类
 * java.lang.Object
 */
public class Loan {
    private double annulInterestRate = 2.5;
    private int numberOfYerar = 1;
    private double loanAmount;
    private LocalDate loanDate = LocalDate.now();
    
    public Loan() {
    }

    public Loan(double annulInterestRate, int numberOfYerar, double loanAmount) {
        this.annulInterestRate = annulInterestRate;
        this.numberOfYerar = numberOfYerar;
        this.loanAmount = loanAmount;
    }

    public double getAnnulInterestRate() {
        return annulInterestRate;
    }

    public void setAnnulInterestRate(double annulInterestRate) {
        this.annulInterestRate = annulInterestRate;
    }

    public int getNumberOfYerar() {
        return numberOfYerar;
    }

    public void setNumberOfYerar(int numberOfYerar) {
        this.numberOfYerar = numberOfYerar;
    }

    public double getLoanAmount() {
        return loanAmount;
    }

    public void setLoanAmount(double loanAmount) {
        this.loanAmount = loanAmount;
    }

    public LocalDate getLoanDate() {
        return loanDate;
    }

    public void setLoanDate(LocalDate loanDate) {
        this.loanDate = loanDate;
    }
    public double getMonthlyPayment() {
        double monthlyRate = this.annulInterestRate / 12;
        //贷款本金×月利率×(1+月利率)^还款月数〕÷〔(1+月利率)^还款月数-1〕
        return loanAmount * monthlyRate * Math.pow((1 + monthlyRate), numberOfYerar * 12)  /
                Math.pow((1 + monthlyRate), numberOfYerar * 12 - 1); 
    }
    public double getTotalpayment() {
        return getMonthlyPayment() * numberOfYerar * 12;
    }
}

  从类的开发者角度来看,设计类时为了让很多不同的用户所使用。为了在更大的应用范围内使用类,类应通过构造方法、属性和方法提供各种方式的定制

下面的程序扮演Loan的用户(使用者)

面向对象的思考

  面向过程的范式重点在于设计方法。面向对象的范式将数据和方法耦合在一起构成对象。

  通过改进我们前面使用程序设计给出的计算身体质量指数的程序来体会面向过程和面向对象程序设计的不同,也可以看出使用对象和类来开发可重用代码的优势

  上面这个对于计算给定体重和身高的身体质量指数是很有用的。但是,它是有局限性的。假设需要将体重和身高同一个人的名字与出生日期关联起来,虽然可以分别使用几个变量来存储这些值,但是这些值不是紧密耦合在一起的。将它们耦合在一起的理想方法就是创建一个包含它们的对象。因为这些值被绑定到单独的对象上,所以它们应该存储在实例数据域中。

  这个例子演示了面向对象范式比面向过程有优势的地方。面向过程的范式重点在于设计方法。面向对象的范式将数据和方法耦合在一起构成对象。使用面向对象程序设计的重点在对象和对象的操作。面向对象方法结合了面向过程范式的功能以及将数据和操作集成在对象中的特性。

  在面向过程程序设计中,数据和数据上的操作是分离的,而且这种做法要求传递数据给方法。面向对象程序设计将数据和对它们的操作都放在一个对象中。这种方法解决了很多面向过程程序设计的固有问题。面向对象程序设计以一种反映真实世界的方式组织程序,在真实世界中,所有的对象和属性及动作都相关联。使用对象提高了软件的可重用性,并且使程序更易于开发和维护。

类的关系

为了设计类,需要探究类之间的关系。类之间的关系通常是关联、聚合、组合以及继承。

关联

  关联是一种常见的关系,描述两个类之间的活动。例如在我们数据库阶段所使用的学员选课系统中学生选取课程是Student类和Course类之间的一种关联,而教师教授课程是Faculty类和Course类之间的关联。UML图形标识如下:

该UML图显示学生可以选取任意数量的课程,教师最多可以教授3门课程,每门课程可以有5到60个学生,并且每门课程只由一位教师来教授。

  关联由两个类之间的实线表示,可以有一个可选的标签来描述关系(上图中,标签是Take和Teach)。每个关系可以有一个可选的小的黑色三角形表明关系的方向。

  关系中涉及的每个类可以有一个角色名称,描述在该关系中担当的角色。Teacher是Faculty的角色名(Teacher是Faculty的角色名)。

  关联中涉及的每个类可以给定一个多重性,放置在类的边上用于给定UML图中关系所涉及的类的对象数。

关联在java代码中如何体现呢?可以通过使用数据域以及方法来实现关联

 

注意:实现类之间的关系可以有很多种可能的方法。例如,Course中的学生和教师信息可以省略(单向关联),因为它们已经在Student和Faculty中了。同样的,如果不需要知道一个学生选取的课程或者教师教授的课程,Student或者Faculty类中的数据域courseList和addCourse方法也可以省略。

聚集和组合

  聚集是关联的一种特殊形式,代表了两个对象之间的归属关系(整体与部分)。聚集建模has-a关系。所有者对象称为聚集对象,它的类称为聚集类。而从属对象称为被聚集对象,它的类称为被聚集类。

   一个对象可以被多个其他的聚集对象所拥有。如果一个对象只归属一个聚集对象,那么它和聚集类之间的关系就称为组合(contains-a)。例如:“一个学生有一个名字”就是学生类Student与名字Name之间的一个组合关系,而“一个学生有一个地址”是学生类Student与地址类Address之间的一个聚集关系,因为一个地址可以被几个学生所共享。在UML图中用实心菱形表示组合,用空心菱形表示聚集:

   聚集关系通常被表示为聚集类中的一个数据域:

  聚集可以存在于同一个类的多个对象之间。例如,我们在练习Oracle的emp员工表时,一个员工可能有一个管理者。

public class Person{
  private Person supervisor;  
}

  由于聚集和组合关系都以同样的方式用类来表示,一般不区分,将两者都称为组合。

泛化(Generalization)

  类之间继承关系(参考下一章节:继承和多态)。

示例学习:设计Course类与Student类

示例学习:设计栈类

  栈(Stack)是一种以“先进后出”的方式存放数据的数据结构,如下图所示:

package com.iweb.demo.client;

/**
 * 自定义栈
 * @author Adan
 *
 */
public class MyStack {
    /**
     * 存储栈中数据的数组
     */
    private Object[] elements;
    /**
     * 栈中元素个数
     */
    private int size;
    public static final int DEFAULT_CAPACITY = 16;
    /**
     * 构建一个默认容量为16的空栈
     */
    public MyStack() {
        this(DEFAULT_CAPACITY);
    }
    
    /**
     * 构建一个指定大小的空栈
     * @param capacity 容量大小
     */
    public MyStack(int capacity) {
        elements = new Object[capacity];
    }
    
    /**
     * 将value压入到栈中
     * @param value value元素
     */
    public void push(Object value) {
        //栈如果存满,需要为栈自动扩容
        if(size >= elements.length) {
            Object[] temp = new Object[elements.length * 2];
            System.arraycopy(elements, 0, temp, 0, elements.length);
            elements = temp;
        }
        elements[size++] = value;
    }
    /**
     * 弹出栈顶元素并将该元素返回
     * @return 栈顶元素
     */
    public Object pop() {
        return elements[--size];
    }
    /**
     * 查看栈顶元素,不删除
     * @return 栈顶元素
     */
    public Object peek() {
        return elements[size - 1];
    }
    /**
     * 是否为一个空栈
     * @return
     */
    public boolean empty() {
        return size == 0;
    }

    /**
     * 获取栈中元素个数
     * @return 元素个数
     */
    public int getSize() {
        return size;
    }
}

猜你喜欢

转载自www.cnblogs.com/adan-chiu/p/12885468.html