组合or继承
面向对象设计有一个原则“优先使用对象组合,而不是继承”。
下面是两者优缺点的比较:
|
组 合 关 系 |
继 承 关 系 |
|
优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 |
缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性 |
|
优点:具有较好的可扩展性 |
缺点:支持扩展,但是往往以增加系统结构的复杂度为代价 |
|
优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 |
缺点:不支持动态继承。在运行时,子类无法选择不同的父类 |
|
优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 |
缺点:子类不能改变父类的接口 |
|
缺点:整体类不能自动获得和局部类同样的接口 |
优点:子类能自动继承父类的接口 |
|
缺点:创建整体类的对象时,需要创建所有局部类的对象 |
优点:创建子类的对象时,无须创建父类的对象 |
我们可以发现继承的缺点远远多于优点,尽管继承在学习OOP的过程中得到了大量的强调,但并不意味着应该尽可能地到处使用它。
相反,使用它时要特别慎重。
只有在清楚知道继承在所有方法中最有效的前提下,才可考虑它。
继承最大的优点就是扩展简单,但大多数缺点都很致命,但是因为这个扩展简单的优点太明显了,很多人并不深入思考,所以造成了太多问题。
虽然笔者的日常工作已经遭遇了太多的Legacy Code因为继承而导致的问题,但是在写新代码时,仍然容易陷入继承而无法自拔。
最近在写得一分新代码:
public interface IEntity{}
public interface IEntityContainer
{
string Name { get;}
int EntityId { get;}
IList<IEntity> EntityContainer{ get;}
}
public class XEntityContainer : IEntityContainer
{
public XEntityContainer()
{
//Name = ...;
//EntityId = ...;
//EntityContainer = ...;
}
public string Name{get; private set;}
public int EntityId{get; private set;}
public IList<IEntity> EntityContainer{ get; private set;}
}
public class YEntityContainer : IEntityContainer
{
public YEntityContainer()
{
//Name = ...;
//EntityId = ...;
//EntityContainer = ...;
}
public string Name { get;private set;}
public int EntityId { get;private set;}
public IList<IEntity> EntityContainer{ get; private set;}
}
Code Review时觉得如果所有的子类都会包含这三行的话,其实是有些duplication的:
public string Name { get;private set;}
public int EntityId { get;private set;}
public IList<IEntity> EntityContainer{ get; private set;}
所以就建议使用组合的方式引入一个新的类型来提供这几项信息。
但,跟着直觉就把Code写成了这样子:
public interface IEntity{}
public interface IEntityContainer
{
string Name { get;}
int EntityId { get;}
IList<IEntity> EntityContainer{ get;}
}
public class EntityContainerBase : IEntityContainer
{
public string Name{get; protected set;}
public int EntityId{get; protected set;}
public IList<IEntity> EntityContainer{ get; protected set;}
}
public class XEntityContainer : EntityContainerBase
{
public XEntityContainer()
{
//Name = ...;
//EntityId = ...;
//EntityContainer = ...;
}
}
public class YEntityContainer : EntityContainerBase
{
public YEntityContainer()
{
//Name = ...;
//EntityId = ...;
//EntityContainer = ...;
}
}
就这样一个好好的二层继承,好好的interface继承,被掰成了三层结构。
比较“坏”的是这种潜意识里依然把继承依然当成了第一选择。
根据同一项目里已有的一段新Code,可以知道将来有些utility方法肯定会不断地往EntityContainerBase里加,直到有一天把它变成Legacy...
我更加倾向于我们应该这样改:
public interface IEntity{}
public struct Container
{
public string Name{get;set;};
public int EntityId{get;set;};
public IList<IEntity> EntityList{get;set;};
}
public interface IEntityContainer
{
string Name { get;}
int EntityId { get;}
IList<IEntity> EntityContainer{ get;}
}
public class XEntityContainer : IEntityContainer
{
public XEntityContainer()
{
//m_Container.Name = ...;
//m_Container.EntityId = ...;
//m_Container.EntityList = ...;
}
public string Name { get{return m_Container.Name;}}
public int EntityId { get{return m_Container.EntityId;};}
public IList<IEntity> EntityContainer{ get{return m_Container.EntityList;};}
private Container m_Container;
}
public class YEntityContainer : IEntityContainer
{
public YEntityContainer()
{
//m_Container.Name = ...;
//m_Container.EntityId = ...;
//m_Container.EntityList = ...;
}
public string Name { get{return m_Container.Name;}}
public int EntityId { get{return m_Container.EntityId;};}
public IList<IEntity> EntityContainer{ get{return m_Container.EntityList;};}
private Container m_Container;
}
多么漂亮的二层结构,没有任何Base类,将来的公共方法,Utility方法不会“无脑”的往Base里塞。
组合or继承的更多相关文章
- 28-React state提升、组件组合或继承
Lifting State Up state提升 对于在React应用程序中更改的任何数据,应该有一个单一的数据源.通常,都是将state添加到需要渲染的组件.如果其他组件也需要它,您可以将其提升到最 ...
- Java(Android)编程思想笔记02:组合与继承、final、策略设计模式与适配器模式、内部类、序列化控制(注意事项)
1.组合和继承之间的选择 组合和继承都允许在新的类中放置子对象,组合是显式的这样做,而继承则是隐式的做. 组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形.即在新类中嵌入某个对象,让其实 ...
- oc随笔二:组合、继承
在oc中如果没有使用ARC的话,手动管理内存一定要注意处理好“野指针”,通常我们在释放指针的指向的地址时,都要将指针赋值为nil,这样能有效的防止野指针.常用的关键字:retain.assign .s ...
- (笔记):组合and继承之访问限制(一)
下面在介绍组合与继承之前,先介绍一下访问限制,访问限制:public.protected.private三者是按照授权的大小排序的.这里有个博客,对这三者有了经典的诠释.http://blog.csd ...
- Effective Java 第三版——18. 组合优于继承
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 面向对象之组合VS继承:继承过时了?
在阅读Effective Java中的第16条时发现了一个有趣的机制或者说是模式,那就是组合(文中翻译为复用,但是作者认为组合更能体现这种模式的精神),并且文中建议使用组合. 那什么是组合, ...
- day 24 二十四、组合、继承、方法重写和重用、super()
一.组合 1.定义:自定义类的对象作为类的属性 A类的对象具备某一个属性,该属性的值是B类的对象 基于这种方式就把A类与B类组合到一起 对象既能使用A类中的数据与功能,也能使用B类中的数据与功能 2. ...
- 用组合取代继承能为 Activity 带来什么
用组合取代继承能为 Activity 带来什么 原文链接 : Composition over Inheritance,What it means for your Activities 原文作者 : ...
- 重新精读《Java 编程思想》系列之组合与继承
Java 复用代码的两种方式组合与继承. 组合 组合只需将对象引用置于新类中即可. 比如我们有一个B类,它具有一个say方法,我们在A类中使用B类的方法,就是组合. public class B { ...
- day25-静态、组合、继承
#!/usr/bin/env python # -*- coding:utf-8 -*- # ----------------------------------------------------- ...
随机推荐
- c语言数据结构之 堆排序
算法:先生成随机数,赋值到数组,将数组第一个元素a[0]设置为哨兵,函数调用数组和随机数个数n,再设定n/2的根结点与孩子结点进行比较操作,若右孩子存在,则选出三个数里最小的数赋值给根节点,如果右孩子 ...
- Delphi常用关键字用法详解
本文详细介绍了Delphi中常用的各个关键字名称及用法,供大家在编程过程中借鉴参考之用.详情如下: absolute: ? 1 2 3 4 5 6 7 8 9 10 //它使得你能够创建一个新变量, ...
- STM32学习笔记——OLED屏
STM32学习笔记--OLED屏 OLED屏的特点: 1. 模块有单色和双色可选,单色为纯蓝色,双色为黄蓝双色(本人选用双色): 2. 显示尺寸为0.96寸 3. 分辨率为128*64 4. ...
- SQLSERVER不带JOIN的语句与带JOIN语句的区别
//1.连接两个表查询 SELECT * FROM table1 t1,table2 t2 //2.连接两个表查询 SELECT * FROM table1 join table2 on table1 ...
- 【转】Java出现No enclosing instance of type E is accessible. Must qualify the allocation with an enclosing
最近在看Java,在编译写书上一个例子时,由于书上的代码只有一部分,于是就自己补了一个内部类.结果编译时出现:No enclosing instance of type E is accessible ...
- 判断ie版本
(function(){ var browser=navigator.appName var b_version=navigator.appVersion var version=b_version. ...
- jsonP跨域调用
-------------------------------------jsonP跨域调用------------------------------------- <div class=&q ...
- TTS通过JavaScript调用
<script type="text/ecmascript"> var VoiceObj = new ActiveXObject("Sapi.SpVoice& ...
- cuda-convnet windows8下编译
编译环境: windows8.1 Anaconda python2.7 Visual studio 2012 CUDA6.0 Pthread for windows Intel Math Kernel ...
- 自己家里搭建NAS服务器有什么好方案?
转自:https://www.zhihu.com/question/21359049 作者:陈二发链接:https://www.zhihu.com/question/21359049/answer/6 ...