组合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里塞。

时间: 2024-10-10 17:54:49

组合or继承的相关文章

day26--静态属性、类方法、静态方法、组合、继承

一.静态属性 class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property def cal_area(self): # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.widt

Python学习之旅—面向对象进阶知识:类的命名空间,类的组合与继承

前言 上篇博客笔者带领大家初步梳理了Python面向对象的基础知识,本篇博客将专注于解决三个知识点:类的命名空间,类的组合以及面向对象的三大特性之一继承,一起跟随笔者老看看今天的内容吧. 1.类的命名空间 在上一篇博客中,我们提到过对象可以动态添加属性,一起来回忆下昨天的知识点,看如下的代码: class A: pass a = A() a.name = 'alex' print(a.name) 这里我们手动为a对象添加了一个属性name,然后直接打印可以得到a对象的名称.通过这个例子,我们可以

组合与继承有什么区别

组合和继承是面向对象中两种代码复用的方式.组合是指在新类里面创建原有类的对象,重复利用已有类的功能.继承是面向对象的主要特性之一,它允许设计人员根据其它类的实现来定义一个类的实现.组合和继承都允许在新的类中设置子对象(subobject),只是组合是显式的,而继承则是隐式的.组合和继承存在着对应关系:组合中的整体类和继承中的子类对应,组合中的局部类和继承中的父类对应. 二者的区别在哪里呢?首先分析一个实例.Car表示汽车对象,Vehicle表示交通工具对象,Tire表示轮胎对象.三者的类关系下图

组合优于继承

原文地址:http://leihuang.org/2014/11/18/composition-inheritance/ 为什么组合优于继承? 这是一个非常典型的设计模式的问题,Head First Design Pattern第一章好像就讲了,之前看得有点忘了.以下我把stackoverflow上面得分比較高的答案搬过来用一下,我认为这样更easy理解些. 两者差别 Think of composition as a has a relationship. A car "has an"

组合查询——继承与多态的小练习

上次个人版机房收费做的时候,组合查询这里做的不好,没有抽出模板,导致感觉有很多冗余,这次自己挑U层,一方面是感觉自己的B层和D层做的可以提升的空间目前很小了,另一方面就是想加强一下自己对细节的注意问题. 上次组合查询是这样的,以代码为例: U层代码如下: Imports System.Collections '引入HashTable类 Public Class frmColWorkInfo Dim HtZD As New Hashtable '定义处理字段名转换的hashtable Dim Ht

Java编程思想笔记02:组合与继承、final、策略设计模式与适配器模式、内部类、序列化控制(注意事项)

1.组合和继承之间的选择 组合和继承都允许在新的类中放置子对象,组合是显式的这样做,而继承则是隐式的做. 组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形.即在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口.为取得此效果,需要在新类中嵌入一个现有类的private对象.但有时,允许类的用户直接访问新类中的组合成分是极具意义的,即将成员对象声明为public.如果成员对象自身都隐藏了具体实现,那么这种做法是安全的.当用户能够了

用组合代替继承能为 Activity 带来什么

用组合代替继承能为 Activity 带来什么 原文链接 : Composition over Inheritance,What it means for your Activities 原文作者 : Josh Brown 译文出自 : 开发技术前线 www.devtf.cn 译者 : chaossss 校对者: Mr.Simple 状态 : 完成 事实上我们在很多 Java 进阶书籍上看到过"开发时应该更倾向于选择组合而不是继承"的建议,为什么建议我们更倾向于而不是完全代替呢,因为当

组合与继承

组合与继承 Java 继承 组合 1. 组合 组合:只需在新的类中产生现有类的对象.由于新的类由现有类的对象组成,所以称为组合.(只是复用了现有程序代码的功能,而非它的形式)如下: class A{ private int a = 10: private int b; private float c; } public class B{ private String str; private A a = new A(); pubblic String tostring(){ System.out

(转)组合优于继承---设计模式之策略模式

文章来源:http://www.javaeye.com/topic/328262 当我们掌握了Java的语法,当我们了解了面向对象的封装.继承.多态等特性,当我们可以用Swing.Servlet.JSP技术构建桌面以及Web应用,不意味着我们可以写出面向对象的程序,不意味着我们可以很好的实现代码复用,弹性维护,不意味着我们可以实现在维护.扩展基础上的代码复用.一把刀,可以使你制敌于无形而于江湖扬名,也可以只是一把利刃而使你切菜平静.Java,就是这把刀,它的威力取决于你使用的方式.当我们陷入无尽

细学设计模式一(组合与继承)

1 <?php 2 abstract class Lesson{ 3 protected $duration; 4 const FIXED = 1; 5 const TIMED = 2; 6 private $costtype; 7 8 function __construct($duration,$costtype){ 9 $this->duration = $duration; 10 $this->costtype = $costtype; 11 } 12 13 function c