软件开发的OOP实践指南_第1页
软件开发的OOP实践指南_第2页
软件开发的OOP实践指南_第3页
软件开发的OOP实践指南_第4页
软件开发的OOP实践指南_第5页
已阅读5页,还剩38页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、软件开发的oop实践指南第二版czw2007-2-12第1章需求分析:追求完美与容忍缺陷指导软件分析和设计的基本思路当项目组面临两难选择时,首先要用鱼和熊掌来识别矛盾的两个对立面。项目组用“小鱼”来比喻那些开发和维护代价较小、结构较简单但是缺乏某些灵活性的设计方案;用“熊掌”来比喻那些灵活、易扩展,但结构复杂,开发和维护成本较高的设计方案。在最终进行抉择的时候,项目组必须坚持如下准则:在满足需求的情况下,尽量选择“小鱼”而舍弃“熊掌”;只有存在无庸置疑的理由时,才选择“熊掌”作为设计方案。需求分析的基本概念与思想1.什么是需求(requirement)ieee在其发布的软件工程标准词汇表中,将

2、需求定义为 ieee, 1990 :用户解决问题或达到目标所需的条件或能力系统或组件为满足合同、标准、规范或其他正式规定文档所需具有的条件或能力一种反映上述两种条件或能力的文档描述2.软件需求功能性需求和非功能性需求ieee将两者分别定义为 thayer, 1990 :功能性需求:一个系统、软件或组件所必须完成的功能,它定义了系统的行为非功能性需求:描述软件如何完成这些功能(难测试,只进行主观评估)3.项目干系人(stakeholder)敏捷建模理论创始人scott w. ambler将其定义为 ambler, 2003 :和项目有直接利益关系的人,包括“直接用户,间接用户,用户经理,高级经理

3、,运营人员,客户支持人员和项目有集成或交互的其他项目开发人员,受软件产品的开发、发布的潜在影响的维护人员等”。4.需求分析“知道要做什么”确定项目的目标和范围根据项目的目标和范围分析出所有的项目干系人提取出所有的非功能性要求分析出所有的功能性要求(一般采用用例分析方法进行)撰写出项目的需求说明书5.基本原则“只实现你真正需要的东西”极限编程(extreme programming)理论中有一个基本原则kibitz, 1999 :“只实现你真正需要的东西,不要去实现你认为需要的东西。”6.挖掘需求“利用具体的软件原型”在具体的软件原型面前,用户可根据自己的想法指出需要改进或需要添加的内容。这种采

4、用简单的原型替代具体项目的做法利于将复杂问题简单化,是迭代开发过程(增加反馈环节)所推崇的有效的手段。7. 撰写需求说明书文档名称xxx需求说明书密级内部文档编号xxxx编写人小w编写日期2003-11-11版本号0.1审核人审核日期总页数批准人批准日期版本修订记录编号日期版本修订人修订内容项目名称xxx项目描述用户简介功能性需求用例系统的局限性非功能性要求值得注意的是:选择简约方案并容忍一定的缺陷是最明智的做法。需求分析过程中,有效的交流与沟通最为重要。应对项目过程中的需求分析的变化做好充分准备。第2章用例分析:海底总动员与云中漫步为什么使用uml用例分析和用例建模是了解用户行为、细化软件需

5、求的关键工作阶段,在用例建模时首选的辅助设计工具是uml语言。简单地说,uml的主要价值在于,它为我们使用面向对象概念进行可视化设计提供了一套丰富的表达和符号描述。需要注意的是,uml只是一种描述语言,其作用只在于忠实地记录和表述程序员的分析结果和设计思想。uml无法告诉开发人员如何进行面向对象分析与设计,也无法提供任何有关设计原则和设计技巧指南;其次,uml本身并无定义一个标准的开发过程,它不关心程序员使用何种开发模型和设计流程,事实上,uml就像是一种程序员使用的世界语,能有效的促进开发人员间的交流和沟通。总之,uml只是开发人员在设计过程中表达思想、进行交流和沟通的一种工具,使用时应该“

6、点到为止”。用例模型1.什么是用例模型在uml中,用例模型是从外部用户和外围系统的角度,分析和考察待开发系统的行为,并通过参与者(actor,可能是最终用户,也可能是外围系统)与系统间的交互关系描述了系统对外提供的功能特性这种参与者与系统功能特性之间的交互关系就是我们所说的“用例(use case)”。2.用例的三要素:用例是由系统的最终用户或外部环境发起的,用例的发起者被称为参与者。每个用例只描述单独的任务,且所描述的任务必须是符合用户意图的、完整的工作内容。用例必须产生一个对用户有意义的结果。3.场景用例的一个执行实例,是用例执行过程中的一条路径。一个用例可能会包括多个场景,如成功的场景,

7、失败的场景等。4.用例模型的应用价值是一种标准语言,易于成为开发人员之间交流和沟通的媒介;可成为我们评估研发工作量的一个标准,特别对于迭代式开发而言;驱动软件的分析和设计逐步细化;测试过程中使用的测试用例特别是那些关注软件功能的测试用例往往也是根据用例模型来确定的。当我们确认用例模型中的每个用例、每个场景(包括失败的场景),都能正常工作时,软件的功能性测试也就基本完成了。用例建模1.用例建模的步骤确定系统边界确定参与者找出所有的用例确定每个用例的级别撰写用例的文字描述 画出以整个系统为对象的顺序图2.确定系统边界和参与者以图书馆借阅系统为例读者图书馆馆员后台服务系统业务服务器数据服务器借阅终端

8、自动借阅终端软硬件系统图书馆借阅系统系统的“边界”就是将系统的功能性能与系统的外部环境分离出来的逻辑分界线。3.确定用例的级别在编写有效用例一书中,cockburn将由高到低的不同级别用例形象地比喻为云朵和风筝、海平面、海平面以下的鱼虾等自然界事物cockburn,2002。参考cockburn的分类方法,我们分级别画出了图书馆借阅系统的用例层次图:使用图书馆还书借书云朵和风筝归还图书查找书目借阅图书预定海平面逾期罚款后台数据库操作小鱼 螃蟹和虾米 河蚌输入信息输入数字改变焦点图书馆借阅系统的用例层次图4.面向方面程序设计(aop)技术面向对象程序设计大师jacobson告诉我们,在实际开发过

9、程里,系统的横向层次通常体现在不同组件的不同级别上,而一个完整的用例经常要纵向跨越处于不同层次上的组件。这种现象会直接造成:同一组件包含不同用例的代码;同一个用例由不同组件实现jacobson将它们称为“纠缠(tangling)”和“混乱(scattering)”jacobson,2004。解决这类问题的有效方法是1997年以后逐渐发展起来的面向方面程序设计(aop)技术,利用aop技术,我们可以在保持组件层次的基础上,将具有通用性的、纵向的用例单元抽象出来,形成可复用的“方面”,以弥补“缺失的环节”,从根本上提高软件的质量。需要注意的是:uml语言只是一种能忠实记录分析结果与设计思想的表述工

10、具。用例模型可以在不同系统边界或不同用例级别上进行,应谨慎选择。用例图、文字描述、顺序图等都是用例分析的有效工具。第3章设计方法:面向过程与面向对象面向过程的方法适合框架系统吗面向过程的设计方法更适合那些以操作功能为主,对复用性和扩展性要求不高的系统软件;对于通用框架系统,面向过程的设计方法是力不从心的。软件的设计质量衡量标准内聚度和耦合度内聚度:表示一个模块、类或函数所承担职责的自相关程度。当一个模块只负责一件事情,就说明这个模块有很高的内聚度;内聚度高的模块易于理解、复用、扩展和维护。耦合度:表示模块和模块之间、类和类之间、函数和函数之间关系的亲密程度。耦合度越高,软件单元间的依赖性也就越

11、强,软件的可复用性、可扩展性和可维护性就会相应地降低。在面向对象的程序设计中,类与类之间的耦合度由它们为完成自己的职责而必须互相发送的消息及消息的参数来决定。概括起来,“低耦合,高内聚”是所有优秀软件的共同特征。关于面向对象的两种误解误解一:与面向过程的设计方法相比,使用面向对象的设计方法设计出来的软件一定具有更高的质量。面向对象和面向过程是两种不同的设计方法,虽然面向对象有诸多好处,但也有它的内在缺陷,例如:当系统中类的数量不断增多时,类与类之间的耦合关系也会像面向过程方法中的函数和函数之间的耦合关系一样难于管理(利用设计模式等技术可有效降低类与类之间的耦合关系)。从某种意义上说,使用何种设

12、计方法并不重要,能力、知识、经验以及项目组在困境中果断的决策才是确保软件质量的关键。误解二:只要学好vc+,就能精通面向对象的设计方法。事实上,编程语言只是我们实现面向对象的设计方案的基本手段,而不是面向对象的理论和方法本身。我们要做的是在设计中贯彻面向对象的基本准则。面向对象的思考1.继承机制是对现实世界中遗传现象的模拟,是我们最大限度复用代码的一种手段。但继承本身存在其缺陷:过大的继承树会造成类与类之间很强的隐性耦合关系,严重降低软件的可读性;继承是在编译期间通过静态绑定实现的,仅使用继承机制就无法在程序运行期间动态改变属性或方法的绑定关系。2. 两种多态性编译时绑定的静态多态性(通过函数

13、重载实现);运行时绑定的动态多态性(通过虚函数和函数覆盖机制实现),是面向对象设计理论的精髓。3.c+语言成员函数的调用和绑定方式一览表代码形式对于虚函数对于非虚函数类名:函数()作用调用指定类的指定函数作用调用指定类的指定函数绑定方式静态绑定绑定方式静态绑定对象名.函数()作用调用指定对象的指定函数作用调用指定对象的指定函数绑定方式静态绑定绑定方式静态绑定引用变量.函数()作用调用被引用对象所属类中的指定函数作用调用引用变量所属类的指定函数绑定方式动态绑定绑定方式静态绑定指针函数()作用调用被引用对象所属类中的指定函数作用调用指针变量所属类的指定函数绑定方式动态绑定绑定方式静态绑定4.客户和

14、服务器客户和服务器的概念揭示了面向对象技术的核心思维方式。即:每个类都是一个独立的组件,都有自己的属性和承担必要的职责,并通过接口向其他类提供特定的服务;客户类通过发送消息的方式请求服务器类完成相应的职责。面向对象的基本原则开闭原则:一个模块对扩展应是开放的,对修改应是关闭的。简单地说,代码模块应很容易地扩展,但在扩展过程中,无须改动已有的代码。完全替换原则:派生类应该能完全替换掉基类。完全替换指的是在需要一个基类指针或基类引用的地方,传递一个派生类的指针或引用,代码也能正常工作。满足完全替换原则的类可以完全透明地被用户调用,此时,用户可不必关心对象的类型,而随意地使用类中的方法。依赖倒置原则

15、:依赖于抽象,而不依赖于具象。这一原则是框架设计的核心原则,也叫控制倒置原则。非循环依赖原则:包和包之间不能有循环依赖关系。如果a包引用了b到中的对象,b包引用了c包中的对象,而c包又引用了a包中的对象,这种依赖关系就是循环依赖关系。循环依赖关系对于系统结构的危害非常大,它会使对个包之间的耦合度大幅度增强。只实现你真正需要的东西,不要去实现你认为需要的东西。不要重复自己:任何代码都只出现一次。“如果代码重复出现三次以上,就应该考虑重构它fowler,2003”。保持简化的设计。让你的代码保持尽可能的清晰,这是发现可能的逻辑错误、进而改正错误的前提。不要轻易相信你认为最有把握的部分。除了公共的库

16、函数或方法外,只要是程序员自定义的函数或方法,在加入程序进行整体调试之前都必须先对它进行测试(单元测试),以确保该函数或方法能够实现预先设想的结果。为人写代码,而不是为机器写代码。好的标识符命名方式、规范的代码格式、简洁的算法等都有助于改进代码的可读性。特别是某些职责清晰、内聚性强的代码无需注释就很容易被人理解,这种代码也常被称为自注释的代码。框架和类库两种最基本的软件复用方式框架是类库的一种扩展形式,框架和类库的最重要区别是:框架是一个“半成品”的应用程序,而类库只包含一系列可被应用程序调用的类。类库为用户提供了一系列可复用的类,但类库总是被动地响应用户的调用请求;框架则会为某一特定目的实现

17、一个基本的、可执行的架构。框架中已经包含了应用程序从启动到运行的主要流程,程序运行时,框架系统自动调用用户实现的功能组件,即,框架系统的行为是主动的。框架是控制倒置原则的完美体现,按照控制倒置的不同方式,框架可以分为以下两类:白盒框架主要使用面向对象语言的继承和多态机制来实现控制倒置,用户必须从框架提供的类中继承出许多派生类,并实现相应的虚函数,才能扩展框架系统。用户在使用白盒框架前,须对框架中的所有类的细节有清晰的认识,故学习的周期较长。黑盒框架主要通过定义组件接口,然后把用户实现的组件融入框架中以实现控制倒置。用户在使用黑盒框架前,只须了解框架的接口,无须了解框架的内部实现细节,故黑盒框架

18、更易于使用。软件的生命周期模型需求分析瀑布模型概要设计分析设计编码单元测试集成测试维护 迭代模型软 迭代周期n件特性 迭代周期2 迭代周期1 时间注:与瀑布模型相比迭代模型更适合面向对象的开发过程第4章模式:变化之美与永恒之道模式的概念建筑模式语言中写道:“每一种模式描述了一个在环境中不断发生的问题,并描述了解决该问题的核心方案,这样就可以多次使用一个解决方案而不必重复劳动”alexander,2002。概括地说,模式是一个被赋予了特定名词的问题和解决方案对,它可被用于新的语境中,并为使用者提供一些处理新情况的建议。通常通过一种结构化的格式将模式撰成文,一般包括以下四个基本元素:模式的名称该模

19、式所能解决的问题解决方案使用该模式的后果(包括优点和缺点)模式的分类架构模式描述了软件系统基本的结构组织策略,架构模式实际上就是关于软件的宏观组织的规模和指南。设计模式描述的是软件系统的某一局部不断重现的核心解决方案。通用职责分配软件模式(grasp模式)描述了在面向对象设计过程中把职责分配给系统中的不同对象的有效经验和基本准则。grasp模式只是对职责分配过程中的设计原则的总结和概括,而没有为每一个模式提供建议的具体结构。设计模式1蕴涵在设计模式中的设计原则、理念 gof,1995 和“面向对象养猪场”案例设计模式最根本的意图是适宜需求变化,我们应只对变化或可能变化的部分使用设计模式,而对于

20、不变的部分滥用设计模式就会造成“过度设计”。大白猪吃()喂猪机器人工作()工作()对每一只大白猪大白猪吃面向对象养猪场养猪场v1.0面向对象养猪场大白猪吃()工作()对每一只猪如果是大白猪大白猪吃如果是长白猪长白猪吃喂猪机器人工作()长白猪吃()养猪场v2.0适宜需求变化尽量针对接口编程,而不要针对实现编程;针对接口编程的组件不需要知道对象的具体类型和实现,只需要知道抽象类定义了哪些接口,这减少了实现上的依赖关系。猪接口吃()工作()对每一只猪猪吃喂猪机器人工作()面向对象养猪场长白猪吃()波中猪吃()大白猪吃()养猪场v3.0针对接口编程优先使用聚合,而不是继承。继承反映的是类之间“是一个”

21、的关系,它在编译期间静态定义,但它可能破坏数据和操作的封装,使派生类对基类产生较强的依赖;聚合反映的是类之间“包含一个”的关系,它是在运行期间动态定义的,由于它不是面向对象语言直接支持的特性,用户必须编写一些代码来完成聚合的功能。如:下面机器人类中的“工作”方法就必须把消息转发给内部聚合的功能对象,即调用功能对象的“工作”方法,这种消息转发的方式又被称为“委托(delegation)”。面向对象养猪场机器人变形(功能)工作()机器人变形(清洁功能)机器人变形(喂猪功能)机器人变形(兽医功能)猪接口吃()长白猪吃()大白猪吃()功能接口工作()兽医功能工作()清洁功能工作()喂猪功能工作()养猪

22、场v4.0优先使用聚合2设计模式的分类类模式主要依赖类和派生类之间的继承关系来实现,通过继承把几个类组织在一起,并把一些行为推迟到派生类中实现。因为继承关系是静态绑定的,类模式可以在编译期间很容易地调整自己,但在运行期间却很难动态地发生变化。对象模式主要依赖于对象之间的聚合关系来实现,通过聚合关系把几个对象组织在一起,并把一些行为分散到不同的对象中实现。因为聚合关系可以动态发生变化,所以,对象模式在运行期间可以很容易地改变自己的行为。目的(考察该设计模式可用于完成什么工作)创建型模式(封装对象的创建过程)结构型模式(处理类或对象的组织结构)行为型模式(指导类和对象间交互及职责分配关系)范围(考

23、察该设计模式主要是用于类还是对象实例)类模式(处理类之间的继承关系)静态的工厂方法模式适配器模式解释器模式模板方法模式对象模式(处理对象间的组织关系)动态的抽象工厂模式适配器模式职责链模式生成器模式桥接模式命令模式原型模式复合模式迭代器模式单件模式装饰模式中介者模式外观模式备忘录模式享元模式观察者模式代理模式状态模式策略模式访问者模式设计模式的分类可以看到,大部分设计模式都是对象模式,而适配器模式是唯一的既可用类模式实现又可用对象模式实现的设计模式。3设计模式的意图和设计要点gof中的23中“核心设计模式”所能适应的变化模式名称英文名称模式所能适应的变化(意图)实现要点抽象工厂模式abstra

24、ct factory 动态地选择创建多组产品对象中的某一组;添加新的产品对象组非常容易定义一个抽象工厂的接口类,由该接口的派生类具体决定创建哪一组产品生成器模式builder 动态地组合创建一个复杂对象的多个步骤,每个步骤也可以很容易地发生变化定义一个抽象生成器的接口类,由该接口的派生类具体决定创建过程的每一步骤如何实现工厂方法模式factory method 对new操作的进一步封装,用户不用提供类名就能创建对象,能动态改变创建出来的对象类型定义工厂方法(虚函数),推迟到派生类再实现该工厂方法原型模式prototype 通过拷贝原型对象来创建新的对象,改变原型对象就能动态地改变产品对象的类型

25、定义克隆方法(虚函数),在所有的产品对象中实现这个克隆方法单件模式singleton 实现只有唯一对象实例的类(和适应变化无关);单件模式在构建整个系统时非常重要用静态数据成员和函数实现适配器模式adapter 把一个接口转换成另一个接口,隔离两个子系统,使它们的变化不会互相影响定义一个目的接口,可以通过继承或聚合被适配器来实现桥接模式bridge 分离类的接口和实现,在接口不变的情况下,可以动态的改变类的实现分别定义抽象的接口和实现的接口,抽象的接口类中聚合一个实现接口的引用,该引用就是连接接口和实现的桥梁复合模式composite 定义整体和部分对象的继承和聚合结构,很容易添加整体或部分的

26、对象整体和部分类继承于同一个接口,整体接口聚合部分接口装饰模式decorator 通过聚合而不是继承的方式扩充一个类的功能;可以动态选择功能实现结构类似于复合模式,但目的是扩充类的功能外观模式facade 为子系统提供一个统一的接口,子系统的变化不会影响系统的其他部分接口类负责把请求转发给子系统中的其他对象享元模式flyweight 共享小对象,解决面向对象设计中对象过多的问题享元工厂通过享元接口管理(聚合)所有的享元对象,客户类必须通过享元工厂访问享元对象代理模式proxy通过代理访问另外一个对象,被代理对象的位置、实现以及变化不会对客户类产生影响代理类和被代理类继承于同一个接口,代理类通过

27、引用聚合被代理类职责链模式chain of responsibility 把请求发送给一条职责链,职责链上的多个对象都有机会处理请求,对象自己决定如何处理该请求;很容易添加、删除或改变职责链上的处理环节定义请求处理接口类,派生类可以决定如何处理该请求,具体的请求处理对象被组织成了一条职责链命令模式command 把请求封装成命令对象,发送者、命令和接收者对象三者之间松散耦合,都容易发生变化,容易实现宏命令、命令的撤消等操作定义命令接口类,具体的命令类可以决定如何处理请求;既可以自己处理,也可以把请求发送给另一个接收者处理解释器模式interpreter 处理类似语言语法的结构,易添加新的语法,

28、以及改变原有语法的组织形式定义语法表达式接口类,具体的语法表达式的组织结构类似复合模式迭代器模式iterate 遍历聚合对象中的元素,不关心这些元素的实现;容器、迭代器和算法之间相互独立,容易定义新的迭代器和算法定义容器和迭代器接口类,具体的迭代器实现访问容器的方法中介者模式mediator 用中介者隔离发送者和接收者,发送者和接收者之间没有耦合,可独立地发生变化定义中介者和协作者接口类,中介者管理所有的协作者之间的关联关系备忘录模式memento 客户类可以存储服务器类的状态,无须知道服务器类的内部结构,服务器类的变化不影响客户类定义一个备忘录类来保存服务器类的状态,备忘录类隔离了客户类和服

29、务器类观察者模式observer 一对多的事件传输结构,发送者不直接依赖与接收者,接收者的变化不影响发送者定义观察者和主体接口类,具体的观察者通过注册机制,在具体的主体类中进行注册状态模式state 对象状态发生变化时,动态地改变行为;易于添加新的状态定义状态接口类,具体的状态类中实现该状态对应的行为,上下文对象中聚合一个当前使用的状态策略模式strategy 封装一系列算法,可动态选择使用哪一种算法;易于添加新的算法定义策略接口类,具体的策略类中实现具体的算法,上下文对象中聚合一个当前使用的策略模板方法模式template method不改变算法的结构就能重新定义算法中的某些步骤抽象类中实现

30、算法的骨架,派生类中实现算法的具体步骤访问者模式visitor 分离一个类结构和定义在这个类结构上的操作。可以在不改变类结构的前提下添加新的操作定义访问者接口,具体的访问者类实现具体的操作4设计模式中的简单和复杂复杂的一面:使用设计模式要求我们添加更多的类,进行更多的设计工作和掌握更多的面向对象设计知识。简单的一面:使用了设计模式后,软件系统会有更好的复用性、扩展性和维护性;当面对变化的需求时,只需修改少量的代码,可以大幅度简化后续的开发和维护工作。第5章分析模型:实体类与软件类面向对象分析与面向对象设计的区别面向对象分析侧重于理解问题,描述软件要做什么;而面向对象设计侧重于理解解决方案,描述

31、软件要如何做。面向对象分析一般只考虑理想的设计,不关心技术和实现层面的细节;而面向对象设计需要得到具体、详尽,接近于真实的代码的设计方案。在设计结果的描述方式上,面向对象分析侧重于描述对象的行为;而面向对象设计侧重于描述对象的属性和方法。面向对象分析只关注功能性需求;面向对象设计既关注功能性需求也关注非功能性需求。面向对象分析产生的系统模型通常规模较小;而面向对象设计产生的系统模型通常规模较大,内容也比较完整、详尽。实体类和软件类jacobson在统一软件开发过程中指出“分析模型中的类可以分为实体类、边界类和控制类,这种分析方式的优点是可以更小、更专门化的类,更容易区分固定不变的对象和易于变化

32、的对象 jacobson, 1999”。1.实体类(entity class)是应用领域中的核心类,一般是从现实世界中的实体对象归纳和抽象出来;实体类的对象实例和应用系统本身有着相同的生命周期。如:pc服务器模拟系统中的cpu类、硬盘类等。2.软件类边界类和控制类的统称边界类(boundary class)是从那些系统和外界进行交互的对象中归纳和抽象出来的,即,边界类是系统内的对象和系统外的参与者的联系媒介,外界的消息只有通过边界类的对象实例才能发送给系统。如:pc服务器模拟系统中的键盘类、鼠标类等。控制类(control class)是实体类和边界类之间的润滑剂,是从控制对象中归纳和抽象出来

33、的,用于协调系统内边界类和实体类之间的交互。如:pc服务器模拟系统中的主板类、总线类和操作系统类等。边界类和控制类的特殊性质在一般的软件系统中,边界类和控制类的对象实例具有唯一性。从这个角度看,边界类和控制类更像是纯软件的东西,是为了协调实体类而添加的类,它们并不是系统想要处理的核心事物。因此,我们将边界类和控制类统称为软件类。3.对实体类和软件类的思考larman在uml和模式应用面向对象分析和设计导论中认为:“在面向对象分析阶段,设计者应努力寻找并提取应用领域中现实存在的概念,而不应关注其他无关的事物 larman, 2002”。这里的“概念”就相当于实体类。寻找软件系统中的实体类和软件类

34、,确定类或对象之间的关系,是面向对象方向工作的核心内容。第6章构架分析:功能分解与对象分析什么是架构分析一般说来,架构分析主要从宏观上考虑一个软件系统应该如何组织。在架构分析工作中,需要确定一些策略性的设计方针、原则和基本模式;架构分析的结果对于后续的面向对象设计工作也是一种约束,有助于消除设计和实现过程中的随意性。因此,架构分析也被称为策略设计。架构分析的作用架构分析工作预先为软件定义了科学的结构和规则,通过这些结构和规则,人们能有效地控制软件的复杂性,使软件易于理解、实现和管理。架构分析可以分离软件中的不同组件,又可以精确定义组件之间的接口,这可以使软件中的大部分组件具备较好的可复用性。架

35、构分析的结果也是多个项目组进行协作的基础。在软件架构中有效分离的组件可以被分配给不同的项目组开发,只要保证组件的接口定义不变,组件内部的变化不会对整个系统的集成产生影响,可以说,架构分析是开展协作式开发的必由之路。架构分析的误区功能分解功能分解和面向对象是格格不入的两种思维方式,如果在面向对象分析前,就采用功能分解的方法将软件系统肢解为多个功能模块,那么,操作同一个数据的方法就可能被拆分到不同的模块中,使模块间产生很强的耦合性。架构模式frank bushman在面向模式的软件构架模式系统中将构架模式定义如下 bushman, 1996 :“架构模式描述了软件系统基本的结构组织策略,它提供了一

36、系列预定义的职责明确的子系统,以及组织这些子系统的关系的规则和指南”。典型的架构模式软件类型架构模式特点和用途系统软件分层layer从不同的层次来观察系统,处理不同层次问题的对象被封装到不同的层中管道和过滤器pipes and filters 用数据流的观点来观察系统。整个系统由一些管道和过滤器组成,需要处理的数据通过管道传送给每一个过滤器,每个过滤器就是一个处理步骤。unix操作系统的管道模型就建立在这样的架构之上。黑板blackboard有两种不同的构件:一种是表示当前状态的中心数据结构;另一种是一组相互独立的构件,这些构件对中心数据进行操作。这种架构主要用于数据库和人工智能系统的开发。分

37、布式系统经纪人broker客户和服务器通过一个经纪人部件进行通信,经纪人负责协调客户和服务器之间的操作,并为客户和服务器发送请求和结果信息。corba就是经纪人模式的典型应用。客户/服务器client/server系统分为客户和服务器,服务器一直处于倾听的状态,客户主动连接服务器,每个服务器可以为多个客户服务。点对点peer to peer系统中的结点都处于平等的地位,一般需要一个中心服务器完成发现和管理结点的操作。icq程序及web service技术的多数应用都是点对点结构。交互系统模型视图控制器model-view-controller当程序的用户界面非常复杂,且关于用户界面的需求很容易

38、变化时,可以把交互类型的软件抽象成模型、视图、控制器这三类组件单元,这种抽象可以很好地分离用户界面和业务逻辑,适应变化的需求。显示抽象控制presentation-abstraction-control这是模型视图控制器模式的另一种变形。分层模式的典型应用显示层(presentation)为用户显示信息中间层(middleware)包括框架系统、数据管理接口及一些与平台无关的服务应用逻辑层(domain logic)封装那些不易发生变化的核心逻辑系统层(system platform)包括操作系统接口、数据库接口、硬件接口等数据持久化(persistent)用于数据处理并记录在文件、数据库等存

39、储位置交互类型软件的三层结构 系统类型软件的两层结构第7章面向对象设计:共性与个性继承的粒度必须基于需求来确定继承的粒度。如果提取对象的某一个共性对于我们要解决的问题没有任何帮助,或只是很小的帮助,就没必要为提取共性而增加继承树的复杂度。对象的某些共性更适合于用属性而不是用新的基类和派生类来表达。利用类和对象模拟实现世界是手段而不是目的,不能为了类而提取类;我们的最终目的是最大限度的实现代码的复用。多重继承的“恐怖菱形”如下图“cpu难题”所示,cisc_cpu、amd_cpu、mmx_cpu、amd_k6_2_cpu这四个类组成了一个菱形结构,这就是多重继承领域里著名的“恐怖菱形”patel

40、,1997。在这种情况下,如果定义一个amd_k6_2_cpu的对象实例,该对象中就会包含两份cisc_cpu的拷贝。为了避免出现两份拷贝,amd_cpu、mmx_cpu两个类从cisc_cpu类继承时,必须使用虚基类机制。cpurisc_cpucisc_cpuintel_cpummx_cpuamd_cpuamd_k6_2_cpucpu难题聚合的粒度使用聚合关系来代替继承关系是解决“cpu难题”的最好选择,我们先看一下使用聚合关系绘制的新类图:我们把mmx_cpu类改名为mmx运算单元类,intel_pentium_4_cpu等类中聚合了一个mmx运算单元类的实例对象。如此,“intel pe

41、ntium mmx cpu是一个mmx cpu”这种暗示继承关系的说法就可转变成“intel pentium mmx cpu包含一个mmx 运算单元”这种暗示了聚合关系的说法。我们完全可以使用聚合关系来代替继承关系,与继承关系的实现代码相比,聚合关系只中多了一些消息转发的代码(这种用法被称为“委托”)。聚合关系也同样存在分解粒度的问题,而在处理集合的粒度问题时,需要遵循的原则与处理继承粒度时的原则一致:模拟实现世界是手段而不是目的,我们的最终目的是改善代码的结构,提高代码的复用性。cisc_cpuintel_cpuamd_cpuamd_k6_2_cpuintel_pentium_4_cpumm

42、x运算单元intel_pentium_mmx_cpuamd_athlon_cpu 使用聚合关系的cpu类图纯粹为代码复用而存在的设计方案设计方案中的某些类无法对应于现实世界中的实际物体或关系,但这些类的存在有助于提高代码的复用性,需要指出的是,应注意这些类在整个类图中是否容易理解,其存在是否有逻辑上的合理性和必然性,若类似的设计会导致程序可读性大幅下降,那这样做就得不偿失了。类结构的重构“重构”一词是fowler在重构一书中最先提出的fowler,2003,书中总结了有关类结构的重构方法:提炼类(extract class)如果一个类的工作应由两个类来完成,就必须创建一个新类,并把相关的属性和

43、方法移到新类中,这样产生的新类和旧类通常以聚合关系联结在一起。类内联化(inline class)当一些类太小,职责过少时,应把该类的属性和方法转移到其他类中,后删除该类,这是提炼类的反向过程。以类取代型别码(replace type code with subclasses)若在一个类中存在一个表达具体对象类型的代码(型别码),该型别码影响了该类的行为,并可能使程序中频繁出现与该型别码相关的分支语句,这时,应该创建新的派生类,并使用多态来代替它。提炼超类(extract super class)若两个类有相同的属性和方法,就必定有一些重复代码,这时,可以创建两个类的公共基类,把重复的属性和方

44、法上移到基类中。折叠继承关系(collapse hierarchy)在重构过程中,有可能发现某个基类和它的派生类非常相似,这时,可以把这两个类合并起来,用属性来表达二者之间的差异,这是提炼子类和提炼超类的反向过程。细化和重组类这是面向对象设计工作的第一步,主要以面向对象分析过程中得到的分析类为基础,以对象继承、聚合粒度相关原则为依据,确定继承树的层次和聚合关系的细化程度。细化和实现类间关系,明确其可见性关系的可见性是指一个对象能“看见”并引用另一个对象的能力。简单地说,可见性规定了对象a能够以何种方式引用对象b:属性可见性对象a的一个属性指向或引用了对象b,这是一种相对持久的关系,只要a存在,

45、就能引用b;参数可见性对象b是对象a中一个方法的参数,a只有在该方法内才能发送消息给b;局部可见性对象b是对象a中一个方法内部定义的局部变量,a只有在该方法内才能发送消息给b;全局可见性对象b是全局变量,对象a任何时候都可以发送消息给对象b。但这会造成很大危害,可以用单件模式来代替全局变量。分配职责,定义执行每个职责的方法职责是“一个类或类型的契约或义务”larman, 2002。一般说来,面向对象系统中的类所承担的职责可以分为两大类:“做”型职责自己要做某事,要为其他对象提供某种服务;协调和控制其他对象的活动。“知道”型职责知道自己内部封装了哪些数据;知道哪些对象与自己发生了关系。通用职责分

46、配软件模式(grasp)描述了在面向对象设计过程中把职责分配给系统中的不同对象的有效经验和基本原则:专家(expert)应该把职责分配给信息专家,即在分配一个职责时,应该了解执行该职责需要哪些信息,这些信息为哪个类或哪些类所有。如果这些信息为一个类所有,就把该职责分配给这个类;若是为多个类所有,就应为每个类分配一个职责,然后通过消息传递和交互,使这些类协同完成该职责。专家模式和面向对象的封装特性是完全一致的:每个类只应该负责处理它所拥有的信息。不要和陌生人说话(dont talk to stranger)一个类尽量只和它的直接对象交互,避免和间接对象进行交互,这样,它就可以和最少的类产生耦合,

47、使整个系统的耦合度保持最低。需要注意的是在面向对象设计过程中,体现对象的个性和归纳它们的共性时,要注意适度的原则,脱离实际的概括和无止境的细化都是相当危险的事情;继承是最强的一种耦合,继承和聚合各有适用范围,取舍的唯一标准是保证设计方案的高内聚、低耦合;合理分配类和对象的职责,有效组织它们的相互关系,这是面向对象设计思想的核心内容。第8章外观模式:统一接口与暴露细节什么是接口java中的接口java语言提供了一个更纯粹的接口类型interface,interface只能包含没有任何实现的接口方法;对于java语言来说,“针对接口编程”更多指的是“针对interface类型编程”。c+中的接口当

48、一个子系统只包含一个具体的c+类时,头文件(*.h)就是这个类的接口,而*.cpp文件就是该接口的具体实现;此外,在c+语言里,一个抽象类的头文件也可以扮演子系统接口的角色;对于c+来说,“针对接口编程”更多指的是“针对抽象类的编程”。外观(facade)模式设计意图外观模式可以为一个子系统中的多个类提供统一的接口;外观模式定义了一个高层次的接口,并使一个子系统更易于使用。在面向对象领域里,引进外观类能为子系统的通用功能提供一个简单、一致的公共接口,并可以最大幅度地降低组件之间的耦合度。基本结构客户类2客户类1 子系统的接口子系统类1类2类3外观类外观模式基本结构上图中,子系统的接口不一定是具

49、体的外观类,它和外观类间的关系可能会有两种:子系统的接口是一个抽象类,不包含任何属性和具体实现,其中定义的所有接口函数都是纯虚函数;而外观类是该抽象类的派生类,隐藏在子系统中。在这种实现方式中,客户程序所看到的只是一个纯粹的借口,而具体的实现都被外观类封装到了子系统的内部。子系统的接口就是外观类的头文件(对于c+而言),客户程序可能会看到一些外观类的私有属性和私有方法需要注意的是外观类一般应以单件类的形式出现,这样,客户程序就可以通过单件类的静态方法获得子系统的唯一接口实例;外观类一般要负责创建和销毁子系统中其他的相关对象,管理其他对象的生命周期;当客户程序调用外观类中的接口函数时,外观类必须

50、知道每一个特定的请求应该发送给子系统中的哪些类或对象;外观模式是面向接口编程中的关键模式,也是封装子系统实现细节的有效手段。第9章观察者模式:间接依赖与直接依赖观察者(observer)模式有效避免“双向依赖”设计意图定义对象之间多对一的依赖关系,即多个对象依赖于一个对象的关系,同时保证,当被依赖的对象状态发生变化时,所有的依赖者会被自动地通知。观察者模式的另一个名称“出版订阅模式”很好地说明了这个设计意图:被依赖的对象向外发布自己的状态,而此前订阅了该状态的所有对象都会得到通知类似消息在对象间的广播。当模块a的变化需要模块b做出相应的修改,而你又不知道有多少个类会被修改时,使用观察者模式就可

51、以切断这种依赖关系,即模块a的变化不再影响模块b,模块b也无须做任何修改。换句话说,这时,模块b对模块a的依赖关系由直接依赖变为间接依赖,具体的依赖关系由模块a动态设定的。因此,两个模块可以在互不干涉的情况下,相对独立的被复用和修改。observerupdate ()subject addobserver ()removeobserver ()notify ()基本结构notify ()for o in observers oupdate ()concretesubjectgetstate ()setstate ()concreteobserverupdate ()观察者模式的实现结构主体类(subject)即被观察者类的基类,它管理所有的观察者,并提供添加和删除观察者的方法,观察

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论