“组合优于继承”——经典案例,简单易懂

前置知识

为了更好的理解“组合优于继承”这个概念,需要先知道下面这几个知识点:

 类和类之间有三种关系:
   a、继承
   b、依赖(在B对象里有个方法,方法里面有个局部变量A,或者入参有A对象,叫做B依赖A)
   c、关联(在B对象里面有个A对象作为成员变量,叫B关联A)
       而”关联“可细分为:
       1)组合(关系强,比如大雁和大雁的翅膀,两者密不可分)
       2)聚合(关系比组合弱,比如大雁飞离雁群,但也可以再飞回来,两者关系没那么强)

前言

组合优于继承中的组合,就没细分了,就是指关联,即可以说:关联优于继承。
下面将用一个经典例子来简明阐述为什么组合会优于继承:

现在有一个需求:

  使用HashSet设计一个新集合,集合中有一个count变量,用,来统计该集合总共存储过多少元素(包括被remove的)
  • 思路一:继承HashSet,重写add()和addAll()方法
class MySet extends HashSet{

    private int count = 0;

    @Override
    public boolean add(Object o) {
      count++;
      return super.add(o);
    }

    @Override
    public boolean addAll(Collection c) {
      count += c.size();
      return super.addAll(c);
    }

    public int getCount(){
      return this.count;
    }

  }

测试:

  public static void main(String[] args) {
    MySet mySet = new MySet();
    mySet.add("a"); mySet.add("b"); mySet.add("c");

    List<String> list = new ArrayList<>();
    list.add("d"); list.add("e"); list.add("f");
    mySet.addAll(list);

    System.out.println("mySet.getCount() = " + mySet.getCount());
  }

输出结果:

mySet.getCount() = 9

发现这个结果是错误的,正确结果是6才对。
原因是:HashSet中的addAll()方法回调了add()方法,导致多count++了

思考:

为了解决上面那个问题,我们可以addAll()方法不重写,这样输出的结果就是正确的了

但是这样做又会有新的问题:假如未来的HashSet类中的addAll()方法不再回调add()方法,那么MySet又出问题了
解决办法:重写addAll()方法,但这次我们不再调用父类的addAll()方法了,而是完全自己重写一个

这次似乎符合需求了,但还有问题是:
  1、假如在未来的jdk版本中,新增了一个往集合添加元素的addSome()方法,
那么我们自己写的MySet方法又失效了(子类继承了addSome()方法却未统计元素的数量(count++))
  2、HashSet类中难免有其他方法调用了add()方法和addAll(),而MySet重写了这两个方法,可能就会导致这些方法出现问题
解决办法:不再重写add()和addAll(),MySet新增两个方法add2()和addAll2()方法,代替add()和addAll()

最后,这样做勉强解决问题了,但是
  1、未来在用这两个方法的时候,就得非常小心,不要用错了,只能调用add2()和addAll2(),不能使用add()和addAll()
  2、万一在未来的HashSet类中,恰好新增了add2()和addAll2()这两个名字的方法,那MySet又出问题了

依据上述:

用继承虽然满足了需求,但终究有些勉强。那用组合试试?

用组合实现:

 class MySet {

    private Set set = new HashSet();
    private int count = 0;

    public boolean add(Object o) {
      count++;
      return set.add(o);
    }

    public boolean addAll(Collection c) {
      count += c.size();
      return set.addAll(c);
    }

    public int getCount(){
      return this.count;
    }
  }

测试:

  public static void main(String[] args) {
    MySet mySet = new MySet(); 
    mySet.add("a"); mySet.add("b"); mySet.add("c");

    List<String> list = new ArrayList<>();
    list.add("d"); list.add("e"); list.add("f");
    mySet.addAll(list);

    System.out.println("mySet.getCount() = " + mySet.getCount());
  }

总结:

如果一步一步跟着上面的思路走,看到这里也就明白了为什么说:组合优于继承。
继承的特性,导致有些时候使用继承并不是一个完美的选择。”冰冻三尺,非一日之寒“平时在写代码的过程中,可以多留意一些
这些细节,但切记不可过分苛求。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值