zoukankan      html  css  js  c++  java
  • 组合or继承

    面向对象设计有一个原则“优先使用对象组合,而不是继承”。

    下面是两者优缺点的比较:

    组 合 关 系

    继 承 关 系

    优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立

    缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性

    优点:具有较好的可扩展性

    缺点:支持扩展,但是往往以增加系统结构的复杂度为代价

    优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象

    缺点:不支持动态继承。在运行时,子类无法选择不同的父类

    优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口

    缺点:子类不能改变父类的接口

    缺点:整体类不能自动获得和局部类同样的接口

    优点:子类能自动继承父类的接口

    缺点:创建整体类的对象时,需要创建所有局部类的对象

    优点:创建子类的对象时,无须创建父类的对象

      

    我们可以发现继承的缺点远远多于优点,尽管继承在学习OOP的过程中得到了大量的强调,但并不意味着应该尽可能地到处使用它。

    相反,使用它时要特别慎重。

    只有在清楚知道继承在所有方法中最有效的前提下,才可考虑它。

     继承最大的优点就是扩展简单,但大多数缺点都很致命,但是因为这个扩展简单的优点太明显了,很多人并不深入思考,所以造成了太多问题。

    虽然笔者的日常工作已经遭遇了太多的Legacy Code因为继承而导致的问题,但是在写新代码时,仍然容易陷入继承而无法自拔。

    最近在写得一分新代码:

     1     public interface IEntity{}
     5     
     6     public interface IEntityContainer
     7     {
     8         string Name { get;}
     9         int EntityId { get;}
    10         IList<IEntity> EntityContainer{ get;}
    11     }
    12     
    13     public class XEntityContainer : IEntityContainer
    14     {
    15         public XEntityContainer()
    16         {
    17             //Name = ...;
    18             //EntityId = ...;
    19             //EntityContainer = ...;
    20         }
    21         
    22         public string Name{get; private set;}
    23         public int EntityId{get; private set;}
    24         public IList<IEntity> EntityContainer{ get; private set;}
    25     }
    26     
    27     public class YEntityContainer : IEntityContainer
    28     {
    29         public YEntityContainer()
    30         {
    31             //Name = ...;
    32             //EntityId = ...;
    33             //EntityContainer = ...;
    34         }
    35         
    36         public string Name { get;private set;}
    37         public int EntityId { get;private set;}
    38         public IList<IEntity> EntityContainer{ get; private set;}
    39     }

    Code Review时觉得如果所有的子类都会包含这三行的话,其实是有些duplication的:

    1 public string Name { get;private set;}
    2 public int EntityId { get;private set;}
    3 public IList<IEntity> EntityContainer{ get; private set;}

    所以就建议使用组合的方式引入一个新的类型来提供这几项信息。

    但,跟着直觉就把Code写成了这样子:

     1     public interface IEntity{}
     5     
     6     public interface IEntityContainer
     7     {
     8         string Name { get;}
     9         int EntityId { get;}
    10         IList<IEntity> EntityContainer{ get;}
    11     }
    12     
    13     public class EntityContainerBase : IEntityContainer
    14     {
    15         public string Name{get; protected set;}
    16         public int EntityId{get; protected set;}
    17         public IList<IEntity> EntityContainer{ get; protected set;}
    18     }
    19     
    20     public class XEntityContainer : EntityContainerBase
    21     {
    22         public XEntityContainer()
    23         {
    24             //Name = ...;
    25             //EntityId = ...;
    26             //EntityContainer = ...;
    27         }
    28     }
    29     
    30     public class YEntityContainer : EntityContainerBase
    31     {
    32         public YEntityContainer()
    33         {
    34             //Name = ...;
    35             //EntityId = ...;
    36             //EntityContainer = ...;
    37         }
    38     }

    就这样一个好好的二层继承,好好的interface继承,被掰成了三层结构。

    比较“坏”的是这种潜意识里依然把继承依然当成了第一选择。

    根据同一项目里已有的一段新Code,可以知道将来有些utility方法肯定会不断地往EntityContainerBase里加,直到有一天把它变成Legacy...

    我更加倾向于我们应该这样改:

     1     public interface IEntity{}
     5     
     6     public struct Container
     7     {
     8         public string Name{get;set;};
     9         public int EntityId{get;set;};
    10         public IList<IEntity> EntityList{get;set;};
    11     }
    12     
    13     public interface IEntityContainer
    14     {
    15         string Name { get;}
    16         int EntityId { get;}
    17         IList<IEntity> EntityContainer{ get;}
    18     }
    19     
    20     public class XEntityContainer : IEntityContainer
    21     {
    22         public XEntityContainer()
    23         {
    24             //m_Container.Name = ...;
    25             //m_Container.EntityId = ...;
    26             //m_Container.EntityList = ...;
    27         }
    28         
    29         public string Name { get{return m_Container.Name;}}
    30         public int EntityId { get{return m_Container.EntityId;};}
    31         public IList<IEntity> EntityContainer{ get{return m_Container.EntityList;};}
    32         
    33         private Container m_Container;
    34     }
    35     
    36     public class YEntityContainer : IEntityContainer
    37     {
    38         public YEntityContainer()
    39         {
    40             //m_Container.Name = ...;
    41             //m_Container.EntityId = ...;
    42             //m_Container.EntityList = ...;
    43         }
    44         
    45         public string Name { get{return m_Container.Name;}}
    46         public int EntityId { get{return m_Container.EntityId;};}
    47         public IList<IEntity> EntityContainer{ get{return m_Container.EntityList;};}
    48         
    49         private Container m_Container;
    50     }

    多么漂亮的二层结构,没有任何Base类,将来的公共方法,Utility方法不会“无脑”的往Base里塞。

  • 相关阅读:
    python-04
    python-03
    python-02
    python-01
    day4-RHCS
    python 之元组(tuple)
    11.21
    python之猜数小游戏
    python之简陋的数据库
    11.20
  • 原文地址:https://www.cnblogs.com/Cmpl/p/5463727.html
Copyright © 2011-2022 走看看