设计模式: 七大原则之 组成优于继承原则

七大原则之 组成优于继承原则

  • 组成优于继承原则
继承就是一个类继承另一类

我们已经知道类和类之间有3中
    a. 继承
    b. 依赖
    c. 关联
        关联可以细问为:
            1). 组合  // <<-- 点题了
            2). 聚合
          所谓的组合模式关系强, 聚合是关系弱


组合优于继承中的组合,其实指的就是关联关系

例子:

  • 需求: 制作一个集合,要求该集合能记录曾今加过多少个元素.(不是统计某个时刻中有多少个元素)
class MySet extends HashSet<Object> {

    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 count;
    }
}

public class AppTest {

    public static void main(String[] args) {
        MySet set = new MySet();

        Set<String> set2 = new HashSet<>();

        set2.add("葵花宝典");
        set2.add("北冥神功");
        set2.add("乾坤大罗移");
        set.addAll(set2);

        int count = set.getCount();
        System.out.println(count);
    }
}
  • 1问题是,在执行了addAll之后,count不是3而是6,为什么呢?因为addAll回调了add方法

  • 针对1的问题, addAll会回调add方法,我们修改代码如下, 把addAll删除调, 不要重写父类的HashSet的addAll了
class MySet extends HashSet<Object> {

    private int count = 0;

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


    public int getCount() {
        return count;
    }
}

public class AppTest {

    public static void main(String[] args) {
        MySet set = new MySet();

        Set<String> set2 = new HashSet<>();

        set2.add("葵花宝典");
        set2.add("北冥神功");
        set2.add("乾坤大罗移");
        set.addAll(set2);
        set.add("独孤九剑");

        int count = set.getCount();
        System.out.println(count);
    }
}
  • 此时,这个代码看起来好像很完美,已经满足了需求了
  • 2问题是: 目前这个代码,必须依赖这样一个事实,就是HashSet的addAll方法必须去回调add方法
    万一在将来的jdk版本中,HashSet的addAll实现代码,突然不再回调add方法了,则在将来的这个jdk版本中,完美自定义的这个MySet就被"撼动"
  • 比如: HashMap,在jdk1.6 1.7 1.8中, 底层实现分别换了3次!

  • 针对b包的问题, MySet必须依赖这样一个事实,addAll必须回掉add,但是jdk未来的版本,不会做这个保证
  • 修改代码如下: 我们自己亲自重写addAll,这次重写addAll,不再让count累加size()了,而是保证addAll一定会调用add
class MySet extends HashSet<Object> {

    private int count = 0;

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

    @Override
    public boolean addAll(Collection<?> c) {
        boolean bb = false;
        for (Object obj : c) {
            if (add(obj)){
                bb = true;
            }
        }
        return bb;
    }

    public int getCount() {
        return count;
    }
}

public class AppTest {

    public static void main(String[] args) {
        MySet set = new MySet();

        Set<String> set2 = new HashSet<>();

        set2.add("葵花宝典");
        set2.add("北冥神功");
        set2.add("乾坤大罗移");
        set.addAll(set2);
        set.add("独孤九剑");

        int count = set.getCount();
        System.out.println(count);
    }
}
  • 此时,这个代码看起来好像很完美,已经满足了需求了
  • 3问题是:
		1. 如果再新的jdk版本中,HashSet突然多了一个元素加入集合的入口方法: addSome
            这个addSome是我们始料未及的,我们的MySet根本没有重写, 新版本中出现的addSome方法,这样,在这个新版本中,
            我们的MySet也继承了addSome方法,当使用addSome方法添加元素时,根本不会去统计元素的数量
            
		2. 我们重写了addAll方法中,和add方法,要知道,在HashSet的所有方法中,难免有一些其它方法,会依赖其它方法,
            会依赖于addAll方法和add方法的,我们没头没脑地重写了别人类中的某些方法,就会导致其它依赖这些方法的方法,出现问题!

  • 针对3提出的2个问题
  • 修改代码如下:
    1. 我们不再重写add和addAll方法了.
    2. 我们额外制作两个代替add和addAll的方法: add2 和 addAll2, 还要在类的API文档中说明.每当要使用add和addAll的时候,
      都去调用add和addAll的方法,都会调用add2和addAll2
class MySet extends HashSet<Object> {

    private int count = 0;


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


    public boolean addAll2(Collection<?> c) {
        boolean bb = false;
        for (Object obj : c) {
            if (add2(obj)){
                bb = true;
            }
        }
        return bb;
    }

    public int getCount() {
        return count;
    }
}

public class AppTest {

    public static void main(String[] args) {
        MySet set = new MySet();

        Set<String> set2 = new HashSet<>();

        set2.add("葵花宝典");
        set2.add("北冥神功");
        set2.add("乾坤大罗移");
        set.addAll2(set2);
        set.add2("独孤九剑");

        int count = set.getCount();
        System.out.println(count);
    }
}
  • 此时,这个代码看起来好像很勉强,但是也满足了需求了
  • 4问题是:
 		1. 目前这种情况对用户要求有点过分,用户必须看类的api文档,看完了还要特别乖乖的使用add2和addAll2,不能写错
 	
        2. 更致命的问题是: 就是那么寸, 在jdk新版本中,HashSet恰恰多了一个api叫做add2和addAll2.

    	继承,已经尽忠了...

  • 针对4提出的2个问题
  • 修改代码如下:
    1. 我们的MySet,再也不要去继承HashSet了.
    2. 取而代之.我们让MySet和HashSet发生关联关系(组合)
class MySet{

    private Set<Object> 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 count;
    }
}

public class AppTest {

    public static void main(String[] args) {
        MySet set = new MySet();

        Set<String> set2 = new HashSet<>();

        set2.add("葵花宝典");
        set2.add("北冥神功");
        set2.add("乾坤大罗移");
        set.addAll(set2);
        set.add("独孤九剑");

        int count = set.getCount();
        System.out.println(count);
    }
}
  • 此时,可以说一声完美
  • 组合的优点,我们已经体会到了
  • 问题是:
    1. 难道以后都不能使用继承了吗?
    2. 难道以后都不能进行方法重写了吗?
	如果父类作者,和子类的作者,不是同一个人,就别继承.
    那么父类作者,不知道未来的子类,会重写自己的哪个方法.
    那么子类作者,不知道,未来的父类, 会加入什么新方法

    如果父类作者,和子类的作者就是同一个人,那就可以放开手脚使用继承了!
    自己当前知道,每个方法都是什么作用,作者可以同时控制父类和子类

    我们自己写代码,继承,重写,随便使用.
    如果我们仅仅是为了复用代码,而继承别人的类, 难免出现"沟通"上的问题.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值