标签:面向对象编程 design patterns 软件设计
摘要
面向对象是设计模式产生的基础。[Gamm94]根据设计模式的特性和适用范围,对设计模式进行了二维的分类。[Zim94b]通过对设计模式间的关系(使用关系、组合关系和相似关系)进行考察,把26中设计模式修正为23种,并对23种设计模式进行了分层划分。[Gamm94]和[Zim94b]对设计模式的分类及关系的考察都脱离了设计模式的基本前提—对象,这直接导致了对设计模式理解上的混乱和使用上的错误。
在[Gamm94]和[Zim94b]的基础上,本文从对象的获取、消息的传递、外观改造、内部元素的探查、行为的泛化等五个基本角度,分析并归类23种设计模式,为设计模式的使用和新模式的产生指明了方向。在编程范式演化分析的基础上,把设计模式提高到编程范式的高度,并提出了面向模式的程序设计。
通过识别、命名与抽象面向对象设计中通用的主题[Gamm94],设计模式已成为交流面向对象设计经验的通用机制。通过识别对象、对象间的协作和责任在对象间的分布来捕获设计背后的意图,并从设计模式的特性和适用范围对模式空间给出了二维的分类[Gamm94],设计模式可以更易于应用于实际的软件开发过程中。[Gamm94]忽略了对设计模式间的关系的考察。
[Zim94b]从设计模式间三种关系(即使用关系、组合关系和相似关系)出发,深刻考察了设计模式对(X,Y)之间关系,修正[Gamm94]中26种设计模式为23种,并根据三种关系对模式进行了分层。[Zim94b]的考察也脱离了对象。
面向对象程序设计的基本出发点是对象。本文从对象的从无到有、主动与被动、由外到内、数据与行为等方面,探讨面向对象设计的复杂性;从获取对象、对象通信、外观对象、内观对象、行为范化等5个基本方面分类23种设计模式,并分析类别内与类别间模式的关系与协作;阐明了何处、如何使用设计模式,并为新模式的产生指明了方向。
软件复杂性是软件的固有属性[10]。编程范式通过各种方式来控制系统的复杂性,但并不能消除或是降低系统的复杂性。在结构化程序设计的基础上,面向过程程序设计使用信息流和数据绑定来控制的复杂性。基于信息流和数据绑定,面向对象程序设计通过封装、继承和多态来控制软件系统的复杂性。但封装、继承和多态只控制了某类对象的复杂性,没有考虑获取对象的复杂性、通信的复杂性及其方式的变化、外观变化、内观变化和行为变化等系统运行特性与维护中的易变因素。
设计模式 |
面向模式程序设计 |
封装、继承和多态 |
面向对象程序设计 |
信息流和数据绑定 |
面向过程程序设计 |
|
结构化程序设计 |
图 1 编程范式演化
设计模式产生的基础是封装、继承和多态三个面向对象程序设计特性。它封装了系统运行特性与维护中的易变因素,是封装、继承和多态对复杂性控制的完整的补充。任何设计模式一但形成,便可独立于封装、继承和多态等面向对象设计特性而独立存在。如可以使用结构化语言C语言来实现设计模式。在面向对象程序设计中,如获取对象、对象通信、外观对象、内观对象、行为范化等5个基本方面均良好地使用了设计模式,称这样的编程范式为面向模式程序设计。设计模式封装了系统运行时特性和维护中的易变因素,面向模式程序设计为系统复杂性分析提供良好的方法。
下面 那部分 都干了什么????
在特定系统中,对象O的单个实例所占用的直接存储空间,即值类型变量所占用的存储空间和引用类型变量所占用的存储空间,是一定的。如不考虑虚拟内存机制,过多的实例对象将会耗尽内存储空间,导致程序崩溃。在面向对象程序设计中,严格控制O的实例个数并建立实例间内存共享机制是高效软件设计的必经之路。从无到有的分析表明,获取对象O实例的复杂性主要表现在四方面:
1)对象选择的复杂性
2)子类型选择的复杂性;
3)构造过程的复杂性;
4)缓存的复杂性。
在GoF设计模式中,可以通过工厂模式、抽象工厂模式、建造者模式、原型模式、单例模式和享元模式等6种设计模式获取一个实例。其中工厂模式和抽象工程模式解决子类型选择的复杂性,建造者模式、单例模式、享元模式和原型模式解决构造过程的复杂性,单例模式和享元模式解决缓存的复杂性。
对象类型选择的复杂性主要表现在依据业务逻辑选择对象类型。使用某个对象与否总是依据当前上下文决定。在传统的面向对象编程中,对象类型是硬编码在特定上下文中的。AOP编程很好的解决了当前上下文与对象类型的选择的解耦问题,我们称其为关注点模式。如选定对象不在本地,可通过两种方式即远程类型加载和动态类型的生成,来获取具体的对象表示。
在面向对象编程中,对象可能有多个子类型,需要根据上下文动态决定创建哪个子类型,具有一个维度的变化因素;多个对象的子类型可能相关或是相互依赖,需要根据上下文动态的决定创建每一子类型系列中的哪个对象,具有多个维度的变化因素。工厂模式定义一个用于创建对象的接口,让子对象决定实例化哪一个子对象,封装了具有一个维度的变化因素;抽象工程模式可封装多个维度的变化因素。
构造过程的复杂性主要表现在复杂对象的构造过程中。建造者模式抓住复杂对象部件组合过程的稳定性和具体部件构造的易变形,将构造过程与表现分离,复用构造过程,实现了各个构造部件子类型的自由选择。原型模式、享元模式和单例模式把构造的复杂性最大的保留在已有的实例中。原型模式和享元模式参数化变易的部分,原型模式通过拷贝原型实例来创建新的对象,享元模式复用已创建的缓存对象。
在程序的某个运行实例中,对象O存在的个数可分为三种情况:存在且仅存在一个实例、存在多个可复用的实例、其它情况。单例模式解决了对象实例存在且仅存在一个的问题;享元模式通过缓存和参数化,实现了实例的复用,有效降低了创建对象的时间开销和大量对象存在所需要的空间开销。在单线程运行环境中,单例模式和享元模式的实现比较简单。但在多线程环境下,需要确保单例模式和享元模式是线程安全的。
按照参与通信的实例个数和方式,实例间的通信可分为:一对一的通信、一对多的通信和链式通信。可以通过函数传参的方式进行对象间的通信,但这种方式在调用对象和被调用对象之间形成硬编码。如函数调用是在同一关注点中,硬编码是可以接受的;如函数调用是在进行关注点的转换,则应禁止硬编码的存在。命令模式和责任链模式都实现了调用者与被调用者之间的解耦。观察者模式通过被调用对象主动注册监听调用对象的某个属性的改变,使调用者可以在不知道被调用对象的情况下独立存在,实现了单方向解耦,被调用对象依然依赖调用对象。
命令模式是一对一的通信模式。封装响应者、命令、参数等信息为一个请求对象,把请求对象参数化以分离请求的调用者和响应者,实现调用者与相应者和具体命名的分离。
责任链模式是链式通信模式。把多个可能处理请求的响应者连成一条链,使请求对象沿着响应者链传递,直到有响应对象处理了该请求。在请求传入责任链后,该模式避免了请求的触发对象和响应对象之间的耦合关系,但第一个响应对象和触发对象之间的调用仍然是硬编码的。
观察者模式是一对多的通信模式。观察者模式实现了单方向解耦,被调用对象依然依赖调用对象。其变种通知模式,通过通知中心的加入实现了调用对象和被调用对象的完全解耦。
组合继承组合模式
称面向对象编程语言提供的根对象类型为Generic;把语言提供的基本数据类型的包装类、字符串和数组称为NGeneric;去除现实世界中的事物的联系,称对其进行孤立抽象所得到的对象为OGeneric,由多个NGeneric对象组合而成;称因多个OGeneric对象相互关联而产生的对象为ORGeneric或RGeneric;称因控制多个对象的关联而产生的控制对象为OCGeneric或BGeneric。
NGeneric集合有自身的复杂性,即集合内部就分为多个等级,如整数是布尔数的超集,逻辑浮点数是整数的超集。我们称在数学上有超集关系的NGeneric类型为不可向上表示的,即不能用有限个布尔数表示任意一个整数,简称为不可表示;如果两个NGeneric类型之间没有超集关系,称这两个类型之间是可表示的。
泛型编程是根据上限制和下限制确定功能代码适用于哪些类型。因此,在面向对象编程中使用泛型技术,任何Generic都可以表现出多态的特性。NGeneric是语言级提供的特性,意义不大;由于业务的复杂性,BGeneric对象系列和OGeneric对象系列及其关系RGeneric对象系列会表现出丰富的多态特性,而这种复杂性将会集中体现于RGeneric对象系列和BGeneric对象系列的复杂性。具体体现于BGeneric对OGeneric接口和RGeneric接口编程,表现出的复杂性与多态性。
GoF23种设计模式中,可通过6种模式来改变一个类型的外观:代理模式、中介模式、适配器模式和门面模式、装饰模式。按照排列的先后顺序,原类型的主体性逐渐丢失,封装类逐渐获得更多的独立性,直到成为其一部分,获取完全的独立性。中介模式不做任何改变,只是协调;适配器模式为单个对象提供一个新的接口;门面模式为多个对象提供一个新的接口;装饰模式添加新的特性,获得了独立性。
代理模式为复杂对象提供一个占位符或是控制访问原始对象的代理。中介模式封装多个对象交互的复杂性,使交互的对象之间不必直接持有其它对象的引用,客户直接使用中介者就可以完成所有的交互需求。中介者只是协调多个对象的通信,降低使用的复杂度,不做任何名称、参数格式、调用结构等方面的改变,不添加任何新的功能;适配器模式修改一个接口的名称或是参数格式或是调用结构等信息,使旧接口满足当前的需求;门面模式定义了一个高层接口,为子系统中的一组接口提供一个一致的界面,使得这一子系统更加容易使用,忽略不用的功能,简化子系统使用的复杂性;装饰模式动态地给一个对象添加一些额外的职责,比子类化更加灵活。
对象作为一个有机的整体,需要探查、了解其内部结构,使其从混沌的非结构状态转换为结构清晰的状态,主要表现在分析与设计阶段中对象结构与行为的分析与设计中。Gof对解释器模式是这样定义的:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。该模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子,有点专家系统的味道;
需要使用不同的对象聚合成新的对象,或是使用同一类对象组合成具有复杂组织结构和交互规则的对象。组合模式用树形结构来表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性;
需要在不暴露内部表示的情况下,遍历聚合的内部元素。迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
需要在不改变对象内部结构的情况下,动态的改变访问其元素的算法。访问者模式表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
行为封装对象的能动机制。备忘录模式、策略模式、状态模式和桥接模式是重要的行为异化模式,它们将某些行为异化为单独的类或是多个类。模板模式是面向对象编程的基本特性,并不能算做一个独立设计模式,只是面向对象的一个多态特性。
备忘录模式的目的在于:在不破坏封装性的前提下捕获一个对象的内部状态、在该对象外部保存这个状态、在需要时能够恢复被保存的状态。其本质是将对象状态的捕获、保存和恢复异化为单独的对象,保证面向对象的封装性不被破坏。
策略模式定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
桥接模式将抽象部分与它的实现部分分离,实现部分具有点到线的变化复杂度。
[Gamm93]E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design patterns: Abstractionand reuse in object-oriented de- signs. In O. Nierstrasz, editor, Proceedingsof ECOOP’93, pa- ges 406–431, Berlin, 1993. Springer-Verlag.
[Gamm94]E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Pattern.Addison-Wesley, To Appear, 1994.
[Zim94b]W.Zimmer, Relationships between Design Patterns, Proceedings of the FirstConference on Pattern Languages and Programming, Addison-Wesley, 1994
[10] F.P. Brooks, No Silver Bullets: Essence and Accidents of SoftwareEngineering, Computer, Vol. 20, No. 4 (Apr 1987) 10-19. ?
[7] G. Booch, Object-Oriented Design with Applications (The Benjamin/CummingsPublishing Company,Redwood City, CA , 1991; ISBN: 0-8053-0091-0). ?
标签:面向对象编程 design patterns 软件设计
原文地址:http://blog.csdn.net/faner200801/article/details/45799683