博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
面向对象编程(十一)——组合以及与继承的区别
阅读量:5922 次
发布时间:2019-06-19

本文共 6637 字,大约阅读时间需要 22 分钟。

组合(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 hoursFirst Turn On: 3 hoursSecond Turn On: 3 hoursNo 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)引用网友的一句很经典的话应该更能让大家分清继承和组合的区别:组合可以被说成“我请了个老头在我家里干活” ,继承则是“我父亲在家里帮我干活"。

转载于:https://www.cnblogs.com/Qian123/p/5176405.html

你可能感兴趣的文章
目前可用的微博秀的嵌入方法大全(亲测2019年2月仍有效)
查看>>
2019年微服务5大趋势,你pick哪个?
查看>>
CNCF案例研究:京东
查看>>
JS中数据类型、内置对象、包装类型对象、typeof关系
查看>>
对是否要用Linux的思考
查看>>
TypeScript--安装依赖,vscode配置ts自动转换成js文件
查看>>
SpiderData 2019年2月15日 DApp数据排行榜
查看>>
Core dump调试小记(暂时未解决)
查看>>
用Python写一份独特的元宵节祝福
查看>>
从零开始的无人驾驶 2
查看>>
PHP面试之网络协议面试题
查看>>
腾讯云CentOS上机摸索(一)--如何在退出SSL后仍保持程序运行
查看>>
git 本地仓库与远程仓库的强制合并 refusing to merge unrelated histories
查看>>
使用midi.js写一个可视化钢琴
查看>>
【EOS】cleos命令
查看>>
吴恩达Coursera机器学习 - Chapter 2 单变量线性回归
查看>>
服务计算 - 5 | GraphQL简单web服务与客户端开发
查看>>
编写git commit信息的最佳实践
查看>>
flask 蓝图总结
查看>>
浅析java内存模型--JMM
查看>>