面向对象设计有一个原则“优先使用对象组合,而不是继承”。
下面是两者优缺点的比较:
组 合 关 系 |
继 承 关 系 |
优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 |
缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性 |
优点:具有较好的可扩展性 |
缺点:支持扩展,但是往往以增加系统结构的复杂度为代价 |
优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 |
缺点:不支持动态继承。在运行时,子类无法选择不同的父类 |
优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 |
缺点:子类不能改变父类的接口 |
缺点:整体类不能自动获得和局部类同样的接口 |
优点:子类能自动继承父类的接口 |
缺点:创建整体类的对象时,需要创建所有局部类的对象 |
优点:创建子类的对象时,无须创建父类的对象 |
我们可以发现继承的缺点远远多于优点,尽管继承在学习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里塞。
时间: 2024-10-10 17:54:49