• 面向对象编程(十一)——组合以及与继承的区别


    组合(has-a 关系)

    我们已经尝试去定义类。定义类,就是新建了一种类型(type)。有了类,我们接着构造相应类型的对象。更进一步,每个类型还应该有一个清晰的接口(interface),供用户使用。

    我们可以在一个新类的定义中使用其他对象。这就是组合(composition)。组合是在Java中实现程序复用(reusibility)的基本手段之一。

    组合:一个对象是另一个对象的数据成员。

    【例子1 code】

    package com.gta.testoop.inherit;
    
    /**@Description:  测试组合    
     * @date 2016-2-1 上午10:30:57    
     */
    //一个源文件可以定义多个类
    //动物Animal类
    public class Animal2 {
        String eye;
    
        public void run(){
            System.out.println("跑跑");
        }
    
        public void eat(){
            System.out.println("吃吃");
        }
    }
    //哺乳动物Mammal类
    class Mammal2 {
        Animal2 animal2;//作为Mammal2的一个属性
        public void taiSheng(){
            System.out.println("我是胎生");
        }
    }
    class Bird {
        Animal2 animal2=new Animal2() ;
        
        public void run(){
            animal2.run();
            System.out.println("我是一个小小小鸟,想要飞的更高");
        }
        public void eggSheng(){
            System.out.println("我是卵生");
        }
    }

    测试类:

    package com.gta.testoop.inherit;
    
    /**@Description: 测试组合
     * @date 2016-2-2 上午9:34:11    
     */
    public class TestComposition {
    
        public static void main(String[] args) {
            Bird b=new Bird();
            b.run();
            b.animal2.eat();
        }
    
    }

    执行结果:

    跑跑
    我是一个小小小鸟,想要飞的更高
    吃吃
    View Code

    【例子2 code】充电筒例子;

    一个充电电筒中的电池、LED灯、按钮…… 都可以是一个对象。我们可以定义一个Battery类来定义和产生电池对象。而在充电电筒的类定义中,可以用一个电池对象作为其数据成员,来代表电池部分的状态。

    我们下面定义一个Battery类,并用power来表示其电量。一个Battery的可以充电(chargeBattery)和使用(useBattery)。我们在随后的Torch类定义中使用Battery类型的对象作为数据成员:

    class Battery 
    {
        public void chargeBattery(double p)
        {
            if (this.power < 1.) {
                this.power = this.power + p;
            }
        }
    
        public boolean useBattery(double p)
        {
            if (this.power >= p) {
                this.power = this.power - p;
                return true;
            }
            else {
                this.power = 0.0;
                return false;
            }
        }
    
        private double power = 0.0;
    }
    
    class Torch
    {
        /** 
         * 10% power per hour use
         * warning when out of power
         */
        public void turnOn(int hours)
        {
            boolean usable;
            usable = this.theBattery.useBattery( hours*0.1 );
            if (usable != true) {
                System.out.println("No more usable, must charge!");
            }
        }
    
        /**
         * 20% power per hour charge
         */
        public void charge(int hours)
        {
            this.theBattery.chargeBattery( hours*0.2 );
        }
    
        /**
         * composition
         */
        private Battery theBattery = new Battery();
    }

    上面的new为theBattery对象分配内存,不可或缺。

    我们定义Battery类。Torch类使用了一个Battery类型的对象(theBattery)来作为数据成员。在Torch的方法中,我们通过操纵theBattery对象的接口,来实现Battery类所提供的功能(functionality)。

    我们说,一个Torch对象拥有(has-a)一个Battery对象。上述关系可以表示成:

    has-a: 手电有电池 (注意上面的菱形连线)

    通过组合,我们可以复用Battery相关的代码。假如我们还有其他使用Battery的类,比如手机,计算器,我们都可以将Battery对象组合进去。这样就不用为每个类单独编写相关功能了。

    我们可以增加一个Test类,看看实际效果:

    public class Test
    {
        public static void main(String[] args)
        {
            Torch aTorch = new Torch();
            System.out.println("Charge: 2 hours");
            aTorch.charge(2);
            System.out.println("First Turn On: 3 hours");
            aTorch.turnOn(3);
            System.out.println("Second Turn On: 3 hours");
            aTorch.turnOn(3);
        }
    }

    执行结果:

    Charge: 2 hours
    First Turn On: 3 hours
    Second Turn On: 3 hours
    No more usable, must charge!
    View Code

    我们通过组合来使用了电池对象所提供的功能,比如探测电量是否用尽(根据useBattery()的返回值)。

    继承(is-a) VS 组合(has-a)

    【组合和继承的综合例子】:

    要实现的目标:鸟(Bird)和狼(Wolf)都是动物(Animal),动物都有心跳(beat()),会呼吸(beat()),但是鸟会fly(fly()),狼会奔跑(run()),用java程序实现以上描述。

    InheritTest.java 使用继承方式实现目标

    CompositeTest.java 使用组合方式实现目标

      1 //InheritTest.java 使用继承方式实现目标
      2 class Animal{
      3     private void beat(){
      4         System.out.println("心脏跳动...");
      5     }
      6     public void breath(){
      7         beat();
      8         System.out.println("吸一口气,呼一口气,呼吸中...");
      9     }
     10 }
     11 //继承Animal,直接复用父类的breath()方法
     12 class Bird extends Animal{
     13     //创建子类独有的方法fly()
     14     public void fly(){
     15         System.out.println("我是鸟,我在天空中自由的飞翔...");
     16     }
     17 }
     18 //继承Animal,直接复用父类的breath()方法
     19 class Wolf extends Animal{
     20     //创建子类独有的方法run()
     21     public void run(){
     22         System.out.println("我是狼,我在草原上快速奔跑...");
     23     }
     24 }
     25 public class InheritTest{
     26     public static void main(String[] args){
     27         //创建继承自Animal的Bird对象新实例b
     28         Bird b=new Bird();
     29         //新对象实例b可以breath()
     30         b.breath();
     31         //新对象实例b可以fly()
     32         b.fly();
     33         Wolf w=new Wolf();
     34         w.breath();
     35         w.run();
     36 /*
     37 ---------- 运行Java程序 ----------
     38 心脏跳动...
     39 吸一口气,呼一口气,呼吸中...
     40 我是鸟,我在天空中自由的飞翔...
     41 心脏跳动...
     42 吸一口气,呼一口气,呼吸中...
     43 我是狼,我在草原上快速奔跑...
     44 
     45 输出完毕 (耗时 0 秒) - 正常终止
     46 */
     47     }
     48 }
     49 
     50 //CompositeTest.java  使用组合方式实现目标
     51 class Animal{
     52     private void beat(){
     53         System.out.println("心脏跳动...");
     54     }
     55     public void breath(){
     56         beat();
     57         System.out.println("吸一口气,呼一口气,呼吸中...");
     58     }
     59 }
     60 class Bird{
     61     //定义一个Animal成员变量,以供组合之用
     62     private Animal a;
     63     //使用构造函数初始化成员变量
     64     public Bird(Animal a){
     65         this.a=a;
     66     }
     67     //通过调用成员变量的固有方法(a.breath())使新类具有相同的功能(breath())
     68     public void breath(){
     69         a.breath();
     70     }
     71     //为新类增加新的方法
     72     public void fly(){
     73         System.out.println("我是鸟,我在天空中自由的飞翔...");
     74     }
     75 }
     76 class Wolf{
     77     private Animal a;
     78     public Wolf(Animal a){
     79         this.a=a;
     80     }
     81     public void breath(){
     82         a.breath();
     83     }
     84     public void run(){
     85         System.out.println("我是狼,我在草原上快速奔跑...");        
     86     }
     87 }
     88 public class CompositeTest{
     89     public static void main(String[] args){
     90         //显式创建被组合的对象实例a1
     91         Animal a1=new Animal();
     92         //以a1为基础组合出新对象实例b
     93         Bird b=new Bird(a1);
     94         //新对象实例b可以breath()
     95         b.breath();
     96         //新对象实例b可以fly()
     97         b.fly();
     98         Animal a2=new Animal();
     99         Wolf w=new Wolf(a2);
    100         w.breath();
    101         w.run();
    102 /*
    103 ---------- 运行Java程序 ----------
    104 心脏跳动...
    105 吸一口气,呼一口气,呼吸中...
    106 我是鸟,我在天空中自由的飞翔...
    107 心脏跳动...
    108 吸一口气,呼一口气,呼吸中...
    109 我是狼,我在草原上快速奔跑...
    110 
    111 输出完毕 (耗时 0 秒) - 正常终止
    112 */
    113     }
    114 }

    总结:

    继承和组合都可以实现代码的复用。

    • "is-a"(是)关系使用继承!
    • "has-a"(拥有)关系使用组合!

    最后总结为以下几点:

    1)组合(has-a)关系可以显式地获得被包含类(继承中称为父类)的对象,而继承(is-a)则是隐式地获得父类的对象,被包含类和父类对应,而组合外部类和子类对应。

    2)组合关系在运行期决定,而继承关系在编译期就已经决定了。

    3)组合是在组合类和被包含类之间的一种松耦合关系,而继承则是父类和子类之间的一种紧耦合关系。
    4)当选择使用组合关系时,在组合类中包含了外部类的对象,组合类可以调用外部类必须的方法,而使用继承关系时,父类的所有方法和变量都被子类无条件继承,子类不能选择。
    5)最重要的一点,使用继承关系时,可以实现类型的回溯,即用父类变量引用子类对象,这样便可以实现多态,而组合没有这个特性。
    6)还有一点需要注意,如果你确定复用另外一个类的方法永远不需要改变时,应该使用组合,因为组合只是简单地复用被包含类的接口,而继承除了复用父类的接口外,它甚至还可以覆盖这些接口,修改父类接口的默认实现,这个特性是组合所不具有的。
    7)从逻辑上看,组合最主要地体现的是一种整体和部分的思想,例如在电脑类是由内存类,CPU类,硬盘类等等组成的,而继承则体现的是一种可以回溯的父子关系,子类也是父类的一个对象。
    8)这两者的区别主要体现在类的抽象阶段,在分析类之间的关系时就应该确定是采用组合还是采用继承。

    9)引用网友的一句很经典的话应该更能让大家分清继承和组合的区别:组合可以被说成“我请了个老头在我家里干活” ,继承则是“我父亲在家里帮我干活"。

  • 相关阅读:
    相关系数
    T检验
    Python模块常用的几种安装方式
    DOM与SAX读取XML方式的不同
    Base64编码
    node.js网页爬虫
    Node.js Express 框架
    Node.js Web 模块
    Node.js GET/POST请求
    Node.js 常用工具
  • 原文地址:https://www.cnblogs.com/Qian123/p/5176405.html
Copyright © 2020-2023  润新知