OO设计中对象的创建和使用(一)
来源:互联网 发布:阿里云cname域名解析 编辑:程序博客网 时间:2024/04/28 05:17
背景
这篇文章是我在Net Objectives工作时写的,我在那里的工作是指导人们编写有效的面向对象程序。本文将介绍一些实用但不同以往的观点,用来解决每天出现的设计问题。本文不关注对象做什么,而是对象的使用和对象的实例化。基于这些观点,可以大规模地简化和改进代码,以满足将来维护的需要。
引用Martin Fowler的观点
在《UML精粹》第三版中,Martin Fowler提出了对象设计的三个层面:概念层、规约层(Specification)、实现层
概念层,概念层中的对象是承担一定职责的实体,通常用抽象类和接口(Interface)来描述,这些实体之间以各种方式相互联系来实现应用系统的目标。 如果我是一个对象,在概念层我关心的是"我的职责是什么"。
规约层,规约层中的对象通过它的公共方法来实现它的职责,每个公共的方法都是对象按照指定的方式提供的服务。
如果我是一个对象,在规约层我关心是"别人如何使用我"
实现层,实现层是对象的代码,对象通过代码来实现它的职责。
如果我是一个对象,在实现层我关心的是"我如何完成我的职责"
要清楚的认识到,先关注什么特性,什么以后再关注,这是一个很有用的做事方法(所有工作都适用)。要成功认知的实践一项复杂活动,这些特性往往是问题的关键。
在软件开发的活动中,内聚性就是这种特性。对于系统内的一个实体,内聚性的念是指这个实体内部的紧密程度。当我们谈到"强内聚"这个术语,也就是指某些的打包在一起的东西属于同一个功能(对方法而言)、同一个职责(对类而言)或是同一个领域(对包而言)。相反,如果把很多不相关的东西混在一个包里,我们称这为弱内聚。例如:一个系统只有一个类,这个类负责所有的事,还含有一个上帝方法,能做所有的事
藕合,是另一个关键特性。这个概念是指系统的实体之间依赖程度。依赖性越强,关系就会越错综复杂,系统也就越难以维护。
对于系统中一个实体,只立足于一个概念层次来工作,有很有益处的。同样的,把自己的思维过程划分成三部分也是有利的:
首先,这样有助于减弱藕合。如果对象之间的关系保持在抽象层面上,后期实现的子类之间的藕合会更弱。这些意见是设计模式的作者"Gang of Four"他们提出的。他们认为设计者应该"面向接口作设计"。
其次,能使系统内聚性更好结构更清楚,因为我们能围绕对象的职责来编写实现细节,而不是其它的方式。这样,一个对象被明确的定义职责范围,而并不是包含一些无关的方法和状态(这对眼前的问题豪无帮助)。
最后,其它开发者能因此有一个清楚的认识过程,因为如果对一个问题,让一个人同时从多个层面去理解它,那么他将很容易泄气。
我的新观点
下面才是正题,我将推荐一些观点,这是些类似的特性,它能帮助我们实现灵活性和健壮性,而这正是我们一直在寻找的面向对象方法。我把我的这些观点概括为:对象创建VS对象使用。
我们看看下面这段代码:
public class SignalProcessor
{
private ByteFilter myFilter;
这是一个SignalProcessor的例子,它使用ByteFilter的一个实现(HiPassFilter)来完成自己的一部分工作。基本上这是不错的作法,内聚性是比较好的,每个类是一个整体,通过和其它类协作来完成自己的任务。并且,对于ByteFiler可以提供多种实现,而不需要改变SignalProcessor的设计。这是一个"可拔插"的设计,并且很容易扩展。
概念上,SignalProcess是负责处理字节数组的信号。规约方面,SignalProcessor表现为一个返回字节数组的process()方法。
而SignalProcessor的实现的方面。我们看到,SignalProcessor调用ByteFilter的实例,在我们经过这里的时候,我们只需考虑它的规约(filter()方法),而不需考虑它的实现。这样很好,干净清楚。
但是,问题在于,SignalProcessor和ByteFilter之间的调用混合了两种不同的概念(创建的概念和使用的概念)。SignalProcessor掌控了HiPassFilter这个类的创建,同时它也使用这个实例来工作。
这看起来好象微不足道,并且实际中也会经常出现。但是,让我思考一下这两个职责,使用对象和制造对象,把它们看做相对独立的事情,并审视它们之间建立的藕合。 关于对象使用的观点
一个客户对象要使用服务对象,要通过服务对象提供的公共方法。如果服务对象被引用为"Object"这种类型,那么就只有一些通用方法可以调用。所以,如果客户对象要使用服务对象,就要满足以下三个条件之一。
·知道这个服务对象的实际类型
·知道这个服务对象实现的某一个接口
·知道这个服务对象继承树上端的一个基类
为了尽可能的降低藕合,我们倾向于满足后两个条件之一,这样,将来根据需要,可以改变实际被使用的客户对象,而不需要改变客户对象的代码。
换句话说,理想情况下,客户对象应该依赖一个抽象,而不是具体现存的一个类,这样将来就可以自由的添加不同的服务类,而不需要维护客户类的代码。特别在服务类被大范围使用的情况下,这种设计原则显示犹为重要。 对象创建的观点
显而易见地,如果我们要避免客户对象知道Bytefilters的具体的实现和这个对象如何创建,这就是意味着,就要有另一个东西,在另一个地方知道这些信息。
因此,我提出一种独特的概念。使用和创建。同样的,客户对象不涉及对象的创建,对象的创建者也不会涉及对象的使用。我们通常称这种"创建者"为工厂类。
这种设计就意味着下面这种设计模型。
需要认真考虑的是创建和使用之间最自然的藕合,做到这一点,任何东西发生变化时都不会提心吊胆地去维护。
如果把ByteFilter的抽象和两个具体实现看作"多态性服务"(ByteFilter是一个服务类,但它有两个版本,而通过多态性这种机制来实现不同版之间的变化),而SignalProcessor这个类只以使用的视角关注这个服务,ByteFilterFactory则是负责不同服务类的创建。
而ByteFilter这个抽象类型(本质上也只是一种抽象)有一些做为接口的公共方法,这个抽象类型是SignalProcessor类和ByteFilter这个多态性服务之间的藕合。而SignalProcessor和两个具体类之间不存在任何的藕合,但前提是我们是一个好的OO程序员,不会给接口随意的添加方法。
ByteFilterFactory和ByteFilter多态性服务之间的藕合就是另一种情况了。这个工厂类和子类建立藕合,因为它必须通过"new"关键字来创建实例。因为这个工厂类很自然地和构造器之间存在藕合,同时也和ByteFilter存在藕合(在把值返回给SignalProcessor之前,需要创建这个类型的引用),但是工厂类却不关心ByteFilter的公共方法,因为工厂类的概念应该只是创建。工厂类创建对象,但绝不调用对象的方法。
这一切带来的结果就是,当客户对象或是工厂类需要改变时,维护工作的痛苦会减少很多。
如果具体的子类要改变,例如ByteFilter需要添加或移除不同的实现,或者某个实现的业务规则发生改变。同时,只要维护一下ByteFilterFactory的代码。而不需要影响SignalProcessor。
如果ByteFilter的接口发生改变,添加、移除或改变公共方法,然后就需要修改一下SignalProcessor,但不会影响ByteFilterFactory。
我们非常感兴趣一个问题,客户对象和工厂类之间有一个很薄弱的环节,那就是ByteFilter这个抽象本身,而不是它的接口。要特别强调这样一个事实,很多资深的设计员都认为正确的抽象是OO设计的关键问题。即使接口有问题,也好过错误的抽象概念。
因此,清晰的划分就意味:实体A和实体B之间的关系,应该仅仅是A创建B或是A使用B,而不能两种关系都有。
这篇文章是我在Net Objectives工作时写的,我在那里的工作是指导人们编写有效的面向对象程序。本文将介绍一些实用但不同以往的观点,用来解决每天出现的设计问题。本文不关注对象做什么,而是对象的使用和对象的实例化。基于这些观点,可以大规模地简化和改进代码,以满足将来维护的需要。
引用Martin Fowler的观点
在《UML精粹》第三版中,Martin Fowler提出了对象设计的三个层面:概念层、规约层(Specification)、实现层
概念层,概念层中的对象是承担一定职责的实体,通常用抽象类和接口(Interface)来描述,这些实体之间以各种方式相互联系来实现应用系统的目标。 如果我是一个对象,在概念层我关心的是"我的职责是什么"。
规约层,规约层中的对象通过它的公共方法来实现它的职责,每个公共的方法都是对象按照指定的方式提供的服务。
如果我是一个对象,在规约层我关心是"别人如何使用我"
实现层,实现层是对象的代码,对象通过代码来实现它的职责。
如果我是一个对象,在实现层我关心的是"我如何完成我的职责"
要清楚的认识到,先关注什么特性,什么以后再关注,这是一个很有用的做事方法(所有工作都适用)。要成功认知的实践一项复杂活动,这些特性往往是问题的关键。
在软件开发的活动中,内聚性就是这种特性。对于系统内的一个实体,内聚性的念是指这个实体内部的紧密程度。当我们谈到"强内聚"这个术语,也就是指某些的打包在一起的东西属于同一个功能(对方法而言)、同一个职责(对类而言)或是同一个领域(对包而言)。相反,如果把很多不相关的东西混在一个包里,我们称这为弱内聚。例如:一个系统只有一个类,这个类负责所有的事,还含有一个上帝方法,能做所有的事
藕合,是另一个关键特性。这个概念是指系统的实体之间依赖程度。依赖性越强,关系就会越错综复杂,系统也就越难以维护。
对于系统中一个实体,只立足于一个概念层次来工作,有很有益处的。同样的,把自己的思维过程划分成三部分也是有利的:
首先,这样有助于减弱藕合。如果对象之间的关系保持在抽象层面上,后期实现的子类之间的藕合会更弱。这些意见是设计模式的作者"Gang of Four"他们提出的。他们认为设计者应该"面向接口作设计"。
其次,能使系统内聚性更好结构更清楚,因为我们能围绕对象的职责来编写实现细节,而不是其它的方式。这样,一个对象被明确的定义职责范围,而并不是包含一些无关的方法和状态(这对眼前的问题豪无帮助)。
最后,其它开发者能因此有一个清楚的认识过程,因为如果对一个问题,让一个人同时从多个层面去理解它,那么他将很容易泄气。
我的新观点
下面才是正题,我将推荐一些观点,这是些类似的特性,它能帮助我们实现灵活性和健壮性,而这正是我们一直在寻找的面向对象方法。我把我的这些观点概括为:对象创建VS对象使用。
我们看看下面这段代码:
public class SignalProcessor
{
private ByteFilter myFilter;
public SignalProcessor()
{
myFilter = new HiPassFilter();
}
public byte[] process(byte[] signal)
{
// Do preparatory steps
myFilter.filter(signal);
// Do other steps
return signal;
}
}
这是一个SignalProcessor的例子,它使用ByteFilter的一个实现(HiPassFilter)来完成自己的一部分工作。基本上这是不错的作法,内聚性是比较好的,每个类是一个整体,通过和其它类协作来完成自己的任务。并且,对于ByteFiler可以提供多种实现,而不需要改变SignalProcessor的设计。这是一个"可拔插"的设计,并且很容易扩展。
概念上,SignalProcess是负责处理字节数组的信号。规约方面,SignalProcessor表现为一个返回字节数组的process()方法。
而SignalProcessor的实现的方面。我们看到,SignalProcessor调用ByteFilter的实例,在我们经过这里的时候,我们只需考虑它的规约(filter()方法),而不需考虑它的实现。这样很好,干净清楚。
但是,问题在于,SignalProcessor和ByteFilter之间的调用混合了两种不同的概念(创建的概念和使用的概念)。SignalProcessor掌控了HiPassFilter这个类的创建,同时它也使用这个实例来工作。
这看起来好象微不足道,并且实际中也会经常出现。但是,让我思考一下这两个职责,使用对象和制造对象,把它们看做相对独立的事情,并审视它们之间建立的藕合。 关于对象使用的观点
一个客户对象要使用服务对象,要通过服务对象提供的公共方法。如果服务对象被引用为"Object"这种类型,那么就只有一些通用方法可以调用。所以,如果客户对象要使用服务对象,就要满足以下三个条件之一。
·知道这个服务对象的实际类型
·知道这个服务对象实现的某一个接口
·知道这个服务对象继承树上端的一个基类
为了尽可能的降低藕合,我们倾向于满足后两个条件之一,这样,将来根据需要,可以改变实际被使用的客户对象,而不需要改变客户对象的代码。
换句话说,理想情况下,客户对象应该依赖一个抽象,而不是具体现存的一个类,这样将来就可以自由的添加不同的服务类,而不需要维护客户类的代码。特别在服务类被大范围使用的情况下,这种设计原则显示犹为重要。 对象创建的观点
显而易见地,如果我们要避免客户对象知道Bytefilters的具体的实现和这个对象如何创建,这就是意味着,就要有另一个东西,在另一个地方知道这些信息。
因此,我提出一种独特的概念。使用和创建。同样的,客户对象不涉及对象的创建,对象的创建者也不会涉及对象的使用。我们通常称这种"创建者"为工厂类。
这种设计就意味着下面这种设计模型。
需要认真考虑的是创建和使用之间最自然的藕合,做到这一点,任何东西发生变化时都不会提心吊胆地去维护。
如果把ByteFilter的抽象和两个具体实现看作"多态性服务"(ByteFilter是一个服务类,但它有两个版本,而通过多态性这种机制来实现不同版之间的变化),而SignalProcessor这个类只以使用的视角关注这个服务,ByteFilterFactory则是负责不同服务类的创建。
而ByteFilter这个抽象类型(本质上也只是一种抽象)有一些做为接口的公共方法,这个抽象类型是SignalProcessor类和ByteFilter这个多态性服务之间的藕合。而SignalProcessor和两个具体类之间不存在任何的藕合,但前提是我们是一个好的OO程序员,不会给接口随意的添加方法。
ByteFilterFactory和ByteFilter多态性服务之间的藕合就是另一种情况了。这个工厂类和子类建立藕合,因为它必须通过"new"关键字来创建实例。因为这个工厂类很自然地和构造器之间存在藕合,同时也和ByteFilter存在藕合(在把值返回给SignalProcessor之前,需要创建这个类型的引用),但是工厂类却不关心ByteFilter的公共方法,因为工厂类的概念应该只是创建。工厂类创建对象,但绝不调用对象的方法。
这一切带来的结果就是,当客户对象或是工厂类需要改变时,维护工作的痛苦会减少很多。
如果具体的子类要改变,例如ByteFilter需要添加或移除不同的实现,或者某个实现的业务规则发生改变。同时,只要维护一下ByteFilterFactory的代码。而不需要影响SignalProcessor。
如果ByteFilter的接口发生改变,添加、移除或改变公共方法,然后就需要修改一下SignalProcessor,但不会影响ByteFilterFactory。
我们非常感兴趣一个问题,客户对象和工厂类之间有一个很薄弱的环节,那就是ByteFilter这个抽象本身,而不是它的接口。要特别强调这样一个事实,很多资深的设计员都认为正确的抽象是OO设计的关键问题。即使接口有问题,也好过错误的抽象概念。
因此,清晰的划分就意味:实体A和实体B之间的关系,应该仅仅是A创建B或是A使用B,而不能两种关系都有。
- OO设计中对象的创建和使用(一)
- OO设计中对象的创建和使用
- OO设计中对象的创建和使用(二)
- OO设计中对象的创建和使用(三)
- 【面向对象设计】OO设计的原则
- OO创建对象
- OO面向对象设计
- OC中类和对象的创建和使用
- OO的概念和设计原则
- 使用oo设计聊天室
- 对象的创建和使用
- js中使用正则表达式(一)创建正则表达式的方式:正则对象方式和正则字面量方式
- Objective-C 中对象的创建和使用
- 基于面向对象(OO)的数据库设计模式探讨
- OO面相对象设计的五大原则
- OO的设计原则
- OO的设计原则
- OO设计的基本原则
- 不允许为非const引用创建临时对象 (关于引用)
- SQL Server 2005基于消息的应用程序介绍
- jsp程序在向sql server中插入数据时出现的问题 急!!!!!!!!
- SQL Server 2005 Express安装问题解答
- 毕业设计 VB.NET
- OO设计中对象的创建和使用(一)
- 使用SQL Server 2000日志转移实现高可用性
- 设计之美——反射加速
- 2008-4-28-Strut2学习
- 郁闷的一天~
- SQL Server应用技巧:内部连接和外部连接中NULLS的影响
- 如何在SQL Server 2005数据库查询中使用CTE
- C++随机数生成
- Scanner和BufferReader对象输入输出