组合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 -*- # ----------------------------------------------------- ...
随机推荐
- PHP MVC
学习一个框架之前,基本上我们都需要知道什么是mvc,即model-view-control,说白了就是数据控制以及页面的分离实现,mvc就 是这样应运而生的,mvc分为了三个层次,而且三个层次各司其职 ...
- Recylerview的使用系列教程
转发自:http://edu.csdn.net/course/detail/2877
- odoo server命令行以及配置文件
odoo server 启动时 def main(args): check_root_user() odoo.tools.config.parse_config(args) check_postgre ...
- chrome浏览器定位页面元素对应代码查找资源
F12 左边箭头或ctrl shift c 点击相应元素即可定位代码 应用:定位flash游戏代码后,鼠标移至带下划线链接处右键copy link
- java 三个循环的优缺点
package cc.knms.appservice.test; import java.text.ParseException; import java.util.ArrayList; import ...
- 使用RXTX获取电脑串口
RXTX是javacomm串口通信的一个扩展 RXTX开发所需文件的下载地址:http://rxtx.qbang.org/wiki/index.php/Download 解压之后可以看到支持各个平台的 ...
- PHP中的运算符---算术运算符、逻辑运算符、赋值运算符、比较运算符
1.算术运算符 常见的算术运算符 运算类型 运算符 举例 结果 取反运算 - -$a 返回$a的负值 加法运算 + $a + $b 返回$a与$b的和 减法运算 - $a - $b 返回$a与$b的差 ...
- SpringMVC——文件上传
----------------------------------------------------------------------------spring.xml-------------- ...
- Date 对象中的 getYear 和 getFullYear方法
生成一个新的日期对象 : var someDate=new Date(); 获取日期月份中的天数: var date=someDate.getDate(); 获取 4 位数的年份: var year= ...
- Understanding Chan Chan's in Go
转自老外的文章:http://tleyden.github.io/blog/2013/11/23/understanding-chan-chans-in-go/ A channel describes ...