继承与多态

规划程序之前要考虑到未来,及时做好应对需求变更的准备。

  良好的面向对象程序设计需要尽量提高程序的扩展性和灵活性,当需求变更或加入新的类型时不至于重写设计或改写太多既有代码。众所周知,继承是面向对象程序设计三大基本特性(封装、继承、多态)之一,充分理解继承的原理机制,对于书写高质量的程序至关重要。

  面向对象程序设计显著特点就是尽量少写重复代码,多考虑既有代码的重构,以此提高程序的扩展性。为什么需要继承机制?说白了就是为了减少重复代码。在设计继承时,我们总会从一系列具有相同属性和行为的类中抽象出共有的部分,然后封装在父类中,以Java方式来说,“子类继承父类”意味着子类拥有父类的实例变量和方法,这部分共有的属性和行为无需重新编写,直接继承即可;当然,子类也可以添加自己特有的实例变量和方法,利用继承机制可以大大程序设计的灵活性。

  以上叙述了这么多,但好像太理论了,完全没有体现出继承的特点和优势。别着急,考虑这样一个案例:

  “假设你需要设计一套动物仿真系统,可以使用户将一群动物丢至特定的环境,并因此模拟出不同动物的状态和行为。现已存在部分被告知的动物的属性和行为,但并不清楚以后会有多少动物被加进来。你需要设计扩展性良好的应用程序,以此应对后期动物园管理员以任意时间引进任意数量动物的需求。”

   好吧,话有点啰嗦,还是直接开始设计吧。

  (1)找出类间具有共同属性和行为的对象,并抽象出共有部分。

    假设你的动物仿真系统需要存储动物的图像(picture)、食物(food)、饥饿程度(hunger)、活动范围的长和宽(boundaries)、活动过程中的X、Y坐标(location)、另外但凡动物均有发声(makeNoise)、饮食(eat)、睡眠(Sleep)、闲逛(roam)的行为。

    假定现在的动物系统中有狮子(Lion)、老虎(Tiger)、猫咪(Cat)、河马(Hippo)、狼(Wolf)、狗(Dog)

 

 (2)抽象出代表共同状态和行为的类

    经以上分析我们抽象出动物(Animal)作为整个系统的顶层父类,并将共有的图像(picture)、食物(food)、饥饿程度(hunger)、活动范围的长和宽(boundaries)、活动过程中的X、Y坐标(location)实例变量和发声(makeNoise)、饮食(eat)、睡眠(Sleep)、闲逛(roam)方法加入Animal这个类中,并利用IDEA自动生成了Get和Set方法。

public class Animal {
    String picture;//动物图像的存储路径
    String food;//目前只有meat和grass两种
    Integer hunger;//饥饿程度
    Double boundaries;//活动区域面积
    Location location = new Location(); //活动坐标

    public void makeNoise() {//发声
        System.out.println("makeNoise method is invoked in Animal....");
    }

    public void eat() { //饮食
        System.out.println("eat method is invoked in Animal....");
    }

    public void sleep() {//睡眠
        System.out.println("sleep method is invoked in Animal....");
    }

    public void roam() { //闲逛
        System.out.println("roam method is invoked in Animal....");
    }

    public String getPicture() {
        return picture;
    }

    public void setPicture(String picture) {
        this.picture = picture;
    }

    public String getFood() {
        return food;
    }

    public void setFood(String food) {
        this.food = food;
    }

    public Integer getHunger() {
        return hunger;
    }

    public void setHunger(Integer hunger) {
        this.hunger = hunger;
    }

    public Double getBoundaries() {
        return boundaries;
    }

    public void setBoundaries(Double boundaries) {
        this.boundaries = boundaries;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }
}

 

(3)确定子类是否需要重写方法和添加自己的实例变量

   观察Animal,容易发现每个动物的eat()方法和makeNoise()方法的方式应该不一样,当然睡眠也可以不同,但为使得描述问题的简单,我们在此假定睡眠一样。以makeNoise()为例,狗狗(Dog)的发声是“汪汪汪”、猫咪(Cat)的发声是“喵喵喵”等等。。根据实际情况,每个子类都可以重写(overload)继承自父类的方法以满足自己的需求。例如Dog子类。

public class Dog extends Animal{
    public void makeNoise() {
        System.out.println("大家好,我是旺财!,我喜欢旺旺旺...");
    }

    public void eat() {
        System.out.println("大家好,我是旺财!,我喜欢鲜美的骨头...");
    }
}

      当然其他的动物也可以根据自己的特性重写继承自父类的方法,或是添加实例变量,在此不再一一列出。

 

 (4)寻找使用共同行为的子类来构造出更多的抽象化机会

   现实世界中,动物本身具有组织化的层次(界、门、纲、目、科、属、种),据此,我们可以抽象出有意义的类。观察发现,现有动物中可以抽象出Canine(猫科)、Feline(犬科)两个科,其中狮子(Lion)、老虎(Tiger)、猫咪(Cat)属于Canine(猫科)、Hippo独立于猫科和犬科、狼(Wolf)、狗(Dog)属于Feline(犬科)。所以我们可以进一步的抽象出Canine和Feline两个类。

public class Canine extends Animal {
    public void roam() {//重写来自Animal的roam()方法
        System.out.println("roam method is invoked in Canine....");
    }
}
public class Feline extends Animal {
    public void roam() { //重写来自Animal的roam()方法
        System.out.println("roam method is invoked in Feline....");
    }
}

 

(5)完成类的继承层次

    根据以上分析,我们可以完成类的继承层次,其中Animal属于顶层父类,Canine(猫科)、Hippo(河马)、Feline(犬科)分别继承自Animal;狮子(Lion)、老虎(Tiger)、猫咪(Cat)继承Canine(猫科);狼(Wolf)、狗(Dog)继承Feline(犬科),由此可得类间继承关系图

   至此,动物仿真系统的所有类间关系就分析整理出来了,当然,每个子类可以任意添加自己的方法或是overload继承自父类的方法,极大的减少了重复代码的书写,并且提高了系统扩展性。

posted @ 2016-11-20 12:17  One160701  阅读(765)  评论(0编辑  收藏  举报