继承和组合

继承和组合都可以实现复用的目的,要根据现实意义来区分用哪一种,就像接口和抽象类的区别,其实这两种方式的开销是差不多的,举个列子如下(摘自《疯狂java讲义》):

  ①继承方式

package 疯狂java讲义;

public class InheritTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Bird bird = new Bird();
        bird.breath();
        bird.fly();
        Wolf wolf = new Wolf();
        wolf.breath();
        wolf.run();
    }

}

class Animal{
    private void beat(){
        System.out.println("心脏跳动");
    }
    public void breath(){
        beat();
        System.out.println("呼吸……");
    }
}

class Bird extends Animal{
    public void fly(){
        System.out.println("鸟会飞行……");
    }
}

class Wolf extends Animal{
    public void run(){
        System.out.println("狐狸会跑……");
    }
}

  ②组合方式

package 疯狂java讲义;

public class CompositeTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Animal1 a1 = new Animal1();
        Bird2 bird = new Bird2(a1);
        Wolf2 wolf = new Wolf2(a1);
        bird.Breath();
        bird.fly();
        wolf.Breath();
        wolf.run();
    }

}
class Animal1{
    private void beat(){
        System.out.println("心脏跳动");
    }
    public void breath(){
        beat();
        System.out.println("呼吸……");
    }
}

class Bird2{
    private Animal1 a;
    public Bird2(Animal1 a){
        this.a = a;
    }
    public void Breath(){
        this.a.breath();
    }
    public void fly(){
        System.out.println("鸟会飞行……");
    }
}

class Wolf2{
    private Animal1 a;
    public Wolf2(Animal1 a){
        this.a = a;
    }
    public void Breath(){
        this.a.breath();
    }
    public void run(){
        System.out.println("狐狸会跑……");
    }
}

  两种方式达到的效果是一致的。但是对于例子中的情况来说是应该用继承的方式的。因为理解应该是:鸟和狐狸是动物的一种,并非说鸟和狐狸是由动物组成的。如果一个人由手,脚,头,身体组成的,这时候就应该使用组合的方式,可以定义五个类,class 人里面是由class 手,class 脚, class 头,class 身体组合而成。继承是is-a的思想,组合是has-a的思想。

  另外,想实现以上的功能,其实也可以不用继承,也不用组合方式,如下:

package 疯狂java讲义;

public class CommonTest {

    public static void main(String[] args) {
        Animal3 a = new Animal3();
        Bird3 bird = new Bird3();
        Wolf3 wolf = new Wolf3();
        bird.Breath(a);
        bird.fly();
        wolf.Breath(a);
        wolf.run();

    }
}

class Animal3{
    private void beat(){
        System.out.println("心脏跳动");
    }
    public void breath(){
        beat();
        System.out.println("呼吸……");
    }
}
class Bird3{ 
    public void Breath(Animal3 a){
        a.breath();
    }
    public void fly(){
        System.out.println("鸟会飞行……");
    }
}

class Wolf3{ 
    public void Breath(Animal3 a){
        a.breath();
    }
    public void run(){
        System.out.println("狐狸会跑……");
    }
}

  那当这几种方法如何取舍呢?其实这就是考究一个人的面向对象的思想,要站在思想的高度去理解问题的本质。如果是继承关系的就用继承,如果是组合关系的就用组合,如果是工具类,就用第三种方法,都是看个人对问题的理解以及考虑以后的扩展去设计。有些人会说,如果这样的话,我何必还要通过传参数的方式把Animal传进来,一下这样也可以啊:

  

package 疯狂java讲义;

public class CommonTest2 {

    public static void main(String[] args) {
        Bird5 bird = new Bird5();
        Wolf4 wolf = new Wolf4();
        bird.Breath();
        bird.fly();
        wolf.Breath();
        wolf.run();

    }
}

class Animal5{
    private void beat(){
        System.out.println("心脏跳动");
    }
    public void breath(){
        beat();
        System.out.println("呼吸……");
    }
}
class Bird5{ 
    public void Breath(){
        Animal5 a = new Animal5();
        a.breath();
    }
    public void fly(){
        System.out.println("鸟会飞行……");
    }
}

class Wolf4{ 
    public void Breath(){
        Animal5 a = new Animal5();
        a.breath();
    }
    public void run(){
        System.out.println("狐狸会跑……");
    }
}

  的确这样也是可以的如果Animal是一个通用类也可以说是工具类的时候,的确是可以这样的,并且这样和方式上一种方式也并没有什么区别,都不是属于继承和组合的方式,只是把Animal当做一个工具来使用,需要的时候就拿过来使用一下,完全是和面向对象无关的,和当前主业务是无关的,只是一个辅助品,就是一个手机,并非我们的必需品,你能拿手机跟我们的手相提并论么,手机要的时候我可以用一下,不需要的时候就丢掉,你自己的手你可以要就接上,不需要的时候就砍掉么?(当然,爱疯除外,很多人要爱疯不要肾……)就是因为手机是一个工具,对人来说只是一个附加品。如果手机只有一个或者要向别人借的,那就通过类外实例化传参数的方式(毕竟不能经常向别人借,这样总不好)。如果手机遍地都是,那就可以需要的时候才实例化一个手机出来用,自己慢慢理解了。既然说到了这里,那就再顺便说下,这就向是线程池或者数据库连接的连接池或者说是内存了,这些都是比较珍贵的资源或者说是对性能影响比较大的,这时候就最好是创建一个,然后能够重复利用,而每次需要的时候又重新创建一个的话,会导致开销很大,浪费很多资源。比如所Animal很大,new那么多对象出来就会很影响性能了。可能有人说,new 一个,在那个函数结束的时候,就已把Animal占用的资源释放了呀,哪有什么影响。其实虽然说作用域是结束了,但是对于像java或.net这些平台来说,作用域结束了,只是没有指针再指向堆里面的这个对象了,要等待GC回收才是真正释放了他所占的内存,所以,如果Animal很大的话,就会导致平台上的线程默认堆栈很快就到达了极限大小,然后触发GC回收,GC也是一个程序,也需要占用CPU,并且还会引起内存重组,防止出现内存碎片,所以如果是大对象或者是比较耗资源的对象就不要频繁地new一个出来,要有线程池和连接池的思想,new一个出来重复利用,这样能减少很多消耗,提高很多性能,所以告诫各位,千万别再一个循环里面new一个大对象,很容易导致宕机的,哈哈~~。但是如果像是string这些类,很小压根不占什么内存的,就可以在需要的时候就直接new一个出来就可以了。(注意:类大不大,是看类的属性多不多,并非方法;重不重要是看类对应管理的计算机资源缺不缺)

  其实这些模式都不是定死的,都是根据具体问题具体分析,不断变化不断发展不断完善的。

原文地址:https://www.cnblogs.com/ismallboy/p/5378379.html