10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

↑↑↑ 欢迎关注,分享更多 IT 技术

注:本笔记为 公司内部技术小组持续学习 2 年多时间 + 个人整理不下 5 次的结果产出。

目录

10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

为什么不推荐使用继承?

组合相比继承有哪些优势?

如何判断该用组合还是继承?


10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

  1. 今天,我们围绕如下三个问题,来详细讲解一下这条设计原则。

    为什么不推荐使用继承?

    组合相比继承有哪些优势?

    如何判断该用组合不是继承?

为什么不推荐使用继承?

  1. 继承是面向对象的四大特性之一,用来表示类间的 is-a 关系,可以解决代码复用的问题。

  2. 继承最大的问题就在于:

    继承层次过深、继承关系过于复杂会影响到代码的可读性和可维护性。

组合相比继承有哪些优势?

  1. 我们可以利用组合、接口、委托三个技术手段,一块儿来解决刚刚继承存在的问题。

  2. 代码

    •    
       public interface Flyable {
         void fly();
       }
       public interface Tweetable {
         void tweet();
       }
       public interface EggLayable {
         void layEgg();
       }
       public class Ostrich implements Tweetable, EggLayable {//鸵鸟
         //... 省略其他属性和方法...
         @Override
         public void tweet() { //... }
         @Override
         public void layEgg() { //... }
       }
       public class Sparrow impelents Flayable, Tweetable, EggLayable {//麻雀
         //... 省略其他属性和方法...
         @Override
         public void fly() { //... }
         @Override
         public void tweet() { //... }
         @Override
         public void layEgg() { //... }
       }
  3. 代码分析

    • 如果只声明方法,不定义实现。也就是说,每个会下蛋的鸟都要实现一遍layEgg() 方法,并且实现逻辑都是一样的。这就导致代码重复的问题。

    • 我们可以针对三个接口定义三个实现类。然后通过组合和委托技术来消除代码重复。

  4. 优化:代码

    •    
       public interface Flyable {
         void fly();
       }
       public class FlyAbility implements Flyable {
         @Override
         public void fly() { //... }
       }
       //省略Tweetable/TweetAbility/EggLayable/EggLayAbility
       ​
       public class Ostrich implements Tweetable, EggLayable {//鸵鸟
         private TweetAbility tweetAbility = new TweetAbility(); //组合
         private EggLayAbility eggLayAbility = new EggLayAbility(); //组合
         //... 省略其他属性和方法...
           
         @Override
         public void tweet() {
           tweetAbility.tweet(); // 委托
         }
           
         @Override
         public void layEgg() {
           eggLayAbility.layEgg(); // 委托
         }
       }
  5. 继承的三个主要作用及其替代手段

    • 继承 替代手段
      表示 is-a 关系 组合和接口的 has-a 关系来替代
      支持多态特性 接口实现
      代码复用 组合和委托来实现
  6. 实践原则

    • 在项目中不用或者少用继承关系,特别是一些复杂的继承关系。

如何判断该用组合还是继承?

  1. 如果类间的继承结构稳定(不会轻易改变),继承层次比较浅,继承关系不复杂,我们就可以大胆地使用继承

  2. 系统越不稳定,继承层次很深,继承关系复杂,我们就尽量使用组合来替代继承。

  3. 一些设计模式固定使用组合或继承

    • 组合

      装饰者模式

      策略模式

      组合模式

    • 继承

      模板模式

  4. 有时,从业务含义上,A 类和B 类并不一定具有继承关系。为了代码复用,硬生生的抽象出一个父类出来,会影响到代码可读性。这时,使用组合就更加合理、更加灵活:

    •    
       public class Url {
         //...省略属性和方法
       }
       ​
       public class Crawler {
         private Url url; // 组合
         public Crawler() {
           this.url = new Url();
         }
         //...
       }
       ​
       public class PageAnalyzer {
         private Url url; // 组合
         public PageAnalyzer() {
           this.url = new Url();
         }
         //..
       }
  5. 还有一些特殊的场景,要求我们必须使用继承

    •    
       public class FeignClient { // feighn client框架代码
         //...省略其他代码...
         public void encode(String url) { //... }
       }
       ​
       public void demofunction(FeignClient feignClient) {
         //...
         feignClient.encode(url);
         //...
       }
       ​
       public class CustomizedFeignClient extends FeignClient {
         @Override
         public void encode(String url) { //...重写encode的实现...}
       }
       ​
       // 调用
       FeignClient client = new CustomizedFeignClient();
       demofunction(client);

参考地址:https://time.geekbang.org/column/intro/100039001

欢迎大家点赞、关注!!!持续分享更多 IT 技术 干货

你可能感兴趣的:(10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?)