• 1. 1软件架构设计模式与实践
  • 2. 目录软件架构视图 软件生命周期与软件架构介绍 架构设计的GRASP模式 质量属性驱动架构设计策略 软件架构模式分析及其实际运用 架构设计原则 面向对象的设计原则 架构设计验证 数据访问层设计(持久层设计) 借鉴RUP中的设计流程 领域模型及业务逻辑层在架构设计中的实现 设计模式本质 SOA的设计思想 软件架构实践 软件系统架构实践与剖析
  • 3. 前言
  • 4. 软件系统开始坏死的症状一个软件系统开始坏死时表现的症状有: 硬化Rigidity——系统变得越来越难以变更,修复或增添新功能的代价高昂; 脆弱Fragility——对系统的任何哪怕是微小的变更都可能造成四处(甚至是与变更处没有逻辑上的关联之处J崩溃; 绑死Immobility——抽取系统的任何部分用来复用都非常困难; 胶着Viscosity——以与原有设计保持一致的方式来对实施变更已经非常困难,诱使开发人员绕过它选择容易但有害的途径,其结果却使系统死的更快。
  • 5. 什么是软件架构 软件架构的概念很混乱。如果你问五个不同的人,可能会得到五种不同的答案。 软件架构概念主要分为两大流派: 组成派:软件架构 = 组件 + 交互。 决策派:软件架构 = 重要决策集。 组成派和决策派的概念相辅相成。
  • 6. 软件架构要层次化并隔离关注点 复杂性是层次化的。 --《人月神话》 好的架构设计必须把变化点错落有致地封装到软件系统的不同部分(即关注点分离)。 通过关注点分离,达到“系统中的一部分发生了变化,不会影响其他部分”的目标。
  • 7. 软件单元的粒度: 粒度最小的单元通常是“类”。 几个类紧密协作形成“模块”。 完成相对独立的功能的多个模块构成了“子系统”。 多个子系统相互配合才能满足一个完整应用的需求,从而构成了软件“系统”。 一个大型企业往往使用多套系统,多套系统通过互操作形成“集成系统”。 软件单元的粒度是相对的。同一个软件单元,在不同场景下我们会以不同的粒度看待它。
  • 8. 架构(Architecture)与框架(Framework)。 框架只是一种特殊的软件,框架也有架构。 可以通过架构框架化达到“架构重用”的目的,如很多人都在用 Spring 框架提供的控制反转和依赖注入来构建自己的架构。
  • 9. 软件架构的作用 如果一个项目的系统架构(包括理论基础)尚未确定,就不应该进行此系统的全面开发。-- Barry Boehm,《Engineering Context》 一个缺陷充斥的系统,将始终是一个缺陷充斥的系统。-- Timothy C. Lethbridge,《面向对象软件工程》 软件架构设计为什么这么难? 因为它是跨越现实世界与计算机世界之间鸿沟的一座桥。 软件架构设计要完成从面向业务到面向技术的转换,在鸿沟上架起一座桥梁。 需求 -> 架构设计 -> 软件架构 -> 系统开发 -> 软件系统
  • 10. 软件架构对新产品开发的作用: 上承业务目标。 下接技术决策。 控制复杂性。 先进行架构设计,后进行详细设计和编码实现,符合“基于问题深度分而治之”的理念。 组织开发。 软件架构方案在小组中间扮演了“桥梁”和“合作契约”的作用。 利于迭代开发和增量交付。 以架构为中心进行开发,为增量交付提供了良好的基础。在架构经过验证之后,可以专注于功能的增量提交。 提高质量。
  • 11. 软件产品线:指具有一组可管理的、公共特性的、软件密集性系统的集合,这些系统满足特定的市场需求或任务需求,并且按照预定义方式从一个公共的核心资产集开发得到。 软件产品线架构:针对一个公司或组织内的一系列产品而设计的通用架构。 软件架构对软件产品线开发的作用: 固化核心知识; 提供可重用资产; 缩短推出产品的周期; 降低开发和维护成本; 提高产品质量; 支持批量定制;
  • 12. 架构师应当为项目相关的不同角色而设计: 架构师要为客户负责,满足他们的业务目标和约束条件。 架构师要为用户负责,满足他们关心的功能需求和运行期质量属性。 架构师必须顾及处于协作分工“下游”的开发人员。 架构师必须考虑“周边”的管理人员,为他们进行分工管理、协调控制和评估监控等工作提供清晰的基础。
  • 13. 软件架构视图——让设计建模更明白、更有效张云贵 2010-05-21
  • 14. “系统架构图”?
  • 15. 架构设计的多重视图 从根本上来说是因为需求种类的复杂性所致。 比如一个媒体发布系统: 功能需求:用户可以通过浏览器浏览媒体的发布。据此初步设计出采用浏览器插件的方案; 约束条件:不能影响用户浏览器的安全性;细化设计方案,需要对插件进行认证,自动判别客户端是否存在,及版本比较;自动下载注册等。 使用期质量属性:为保证浏览的流畅,应减少中间等待的时间,因此应对下一步需使用的媒体做预测等。 制作发布期的质量保证:保证在遇到较大的媒体时能保持浏览的流畅,应在发布时将视频等流式化。
  • 16. 软件系统的需求种类复杂
  • 17. 什么是软件架构视图 个架构视图是对于从某一视角或某一点上看到的系统所做的简化描述,描述中涵盖了系统的某一特定方面,而省略了于此方面无关的实体。 架构要涵盖的内容和决策太多了,超过了人脑“一蹴而就”的能力范围,因此采用“分而治之”的办法从不同视角分别设计;同时,也为软件架构的理解、交流和归档提供了方便。 多视图方法是软件架构归档的方法,更是指导我们进行架构设计的思维方法。
  • 18. (本页无文本内容)
  • 19. 逻辑架构 逻辑架构关注功能。其设计着重考虑功能需求。 开发架构 开发架构关注程序包。其设计着重考虑开发期质量属性,如可扩展性、可重用性、可移植性、易理解性和易测试性等。 运行架构 运行架构关注进程、线程、对象等运行时概念,以及相关的并发、同步、通信等问题。 其设计着重考虑运行期质量属性,例如性能、可伸缩性、持续可用性和安全性等。 物理架构 物理架构关注软件系统最终如何安装或部署到物理机器。其设计着重考虑“安装和部署需求”。以及如何部署机器和网络来配合软件系统的可靠性、可伸缩性等要求。 数据架构 数据架构关注持久化数据的存储方案。设计着重考虑“数据需求”。
  • 20. 关系逻辑视图。逻辑视图不仅关注用户可见的功能,还包括为实现用户功能而必须提供的“辅助功能模块”;它们可能是逻辑层、功能模块等。 开发视图。开发视图关注程序包,不仅包括要编写的源程序,还包括可以直接使用的第三方SDK和现成框架、类库,以及开发的系统将运行于其上的系统软件或中间件。开发视图和逻辑视图之间可能存在一定的映射关系:比如逻辑层一般会映射到多个程序包等。 运行视图。和开发视图的关系:开发视图一般偏重程序包在编译时期的静态依赖关系,而这些程序运行起来之后会表现为对象、线程、进程,运行视图比较关注的正是这些运行时单元的交互问题。 物理视图。和运行视图的关系:运行视图特别关注目标程序的动态执行情况,而物理视图重视目标程序的静态位置问题;物理视图是综合考虑软件系统和整个IT系统相互影响的架构视图。
  • 21. (本页无文本内容)
  • 22. (本页无文本内容)
  • 23. 软件生命周期与软件架构介绍
  • 24. 24软件架构师的定位系统架构师的职责: 一、理解系统的业务需求,制定系统的整体框架(包括:技术框架和业务框架) 二、对系统框架相关技术和业务进行培训,指导开发人员开发。并解决系统开发、运行中出现的各种问题。 系统架构师的目的: 对系统的重用、扩展、安全、性能、伸缩性、简洁等做系统级的把握。 系统架构师能力要求: 一、系统架构相关的知识和经验。 二、很强的自学能力、分析能力、解决问题的能力。 三、写作、沟通表达、培训。
  • 25. 25角色 软件架构师Software Architect 定义 主导系统全局分析设计和实施、负责软件构架和关键技术决策的角色
  • 26. 26职责 领导与协调整个项目中的技术活动(分析、设计和实施等) 推动主要的技术决策,并最终表达为软件构架 确定和文档化系统的相对构架而言意义重大的方面,包括系统的需求、设计、实施和部署等“视图” 确定设计元素的分组以及这些主要分组之间的接口 为技术决策提供规则,平衡各类涉众的不同关注点,化解技术风险,并保证相关决定被有效的传达和贯彻 理解、评价并接收系统需求 评价和确认软件架构的实现
  • 27. 27专业技能 技术全面、成熟练达、洞察力强、经验丰富,具备在缺乏完整信息、众多问题交织一团、模糊和矛盾的情况下,迅速抓住问题要害,并做出合理的关键决定的能力。 具备战略性和前瞻性思维能力,善于把握全局,能够在更高抽象级别上进行思考。 对项目开发涉及的所有问题领域都有经验,包括彻底地理解项目需求,开展分析设计之类软件工程活动等。 具备领导素质,以在各小组之间推进技术工作,并在项目压力下做出牢靠的关键决策。 拥有优秀的沟通能力,用以进行说服、鼓励和指导等活动,并赢得项目成员的信任。
  • 28. 28以目标导向和主动的方式来不带任何感情色彩地关注项目结果,构架师应当是项目背后的技术推动力,而非构想者或梦想家(追求完美) 精通构架设计的理论、实践和工具,并掌握多种参考构架、主要的可重用构架机制和模式。 具备系统设计员的所有技能,但涉及面更广、抽象级别更高。
  • 29. 29软件架构师的知识体系软件架构师作为整个软件系统结构的总设计师,其知识体系、技能和经验决定了软件系统的可靠性、安全性、可维护性、可扩展性和可移植性等方面的性能。因此一个优秀的软件架构师必须具备相当丰富的知识、技能和经验。 通过对比软件架构师和系统分析师在软件开发中的职责和角色,不难发现软件架构师与系统分析师所必需的知识体系也是不尽相同的,系统分析师的主要职责是在需求分析、开发管理、运行维护等方面,而软件架构师的重点工作是在架构与设计这两个关键环节上。因此在系统分析师必须具备的知识体系中对系统的构架与设计等方面知识体系的要求就相对低些;而软件架构师在需求分析、项目管理、运行维护等方面知识的要求也就相对低些。
  • 30. 30成为一名合格的软件架构师必须具备的知识 信息系统综合知识体系 软件架构知识体系
  • 31. 31?MFC,MSF,MOF,RUP,J2EE,Spring,SOA,JUnit,ORM,.Net MVC,UML,XML,Corba,MDA,MDD,Web-Service RSS,Web2.0,AJAX,Serverlet,Hibernate IOC, AOP Ruby On Rails Rup BPEL Workflow Engine LBS Oracle CMMI MQ …
  • 32. 32软件架构师在干什么?思考、思考、再思考 深入理解、准确把握建设的业务需求 分析所有可见的问题、障碍、风险 充分参考已有的成功方案,降低风险 交流、讨论、博弈、质疑 对构思中的方案不断提出质疑,避免漏洞 广泛听取各层面的意见,开拓思路 反复质疑、逐步完善已有的设计构思 在动手实现之前验证设计方案的正确性
  • 33. 33软件架构师的知识结构软件知识 最好要有系统开发全过程经验。 对 IT 建设生命周期各个环节有深入了解,包括:系统/模块逻辑设计、物理设计、代码开发、项目管理、测试、发布、运行维护等。 深入掌握1-2种主流技术平台上开发系统的方法。 了解多种应用系统的结构。 了解架构设计领域的主要理论、流派、框架。
  • 34. 34软件架构师的知识结构业务知识 深入了解系统建设的业务需求。 了解系统的非功能需求和运行维护需求。 了解企业 IT 公共设施、网络环境、外部系统。
  • 35. 35软件架构师的思维方式基于框架的思维 架构设计的层次(Enterprise, Application, etc) IT 的生命周期(What, Why, Where, How, When, etc) 成功经验以及方法论的指导 合理把握技术细节 把握各个层次应有的内容 合理忽略不应有的技术细节
  • 36. 36软件架构师的思维方式风险管理意识 采用成功经验、避免不应有的风险 多方位的开放思维 多维度、多方向、包容性、避免排他性 分析、质疑、抽象、归纳 没有绝对好的架构设计,只有相对优秀的方案
  • 37. 注意:并不存在通用的设计方法。我们认为,给定的某种固定的方法总是会顾此失彼,而且这种不平衡性不太容易觉察。如果给定的某种方法所注重的方面与系统需求不吻合,则这种方法就会将设计引入歧途。所以我们给出的是一些技巧,任何一次设计过程,都需要设计师仔细分析系统的需求和相关的约束条件,灵活运用众多的设计技巧,针对不同的设计方案进行取舍,做出合理的决策。37
  • 38. 为了有效地控制设计工作的复杂性,一般把设计活动分为总体设计和详细设计两个层次。总体设计主要关注于体系结构和接口这些全局性的内容,而详细设计主要关注于每个模块内部的数据结构和算法。至于数据,则在设计的各个层次都会涉及。 软件设计是一个迭代的过程,是一个逐步细化和求精的过程。因此,总体设计和详细设计之间的划分并不是绝对的。哪些是总体设计任务,哪些是详细设计任务,取决于设计师对于整个项目的把握,并没有一个统一的标准。设计师在设计时实际是在做一系列的设计决策,来满足系统的行为目标,质量目标和开发目标。如果一个结构对于完成这些目标非常重要,则它是构架的一部分。相反,如果它可以留到以后再完善,则它不是构架的组成部分。因此,总的说来,一个设计是不是属于构架设计,要看你当前的设计目标。 38
  • 39. 管理陷阱 随着管理性责任的增加,你所从事技术性工作的时间就会显著减少。这样,因为在完成技术性任务和保持职业技能上花费时间的减少,你可能会失去技术优势。 软件架构师和管理者有很大的差异:软件架构师是直接的技术贡献者;而管理者则是通过协调其他人员的活动来间接做出贡献。他们往往一起协作,构成高效的管理团队。以经验看,把两者结合起来却不能长久地工作。 作为一名软件架构师,如果你已经建立起个人的职业原则的话,就有可能避免成为管理者。
  • 40. 架构和设计应该做到何种程度软件架构必须设计到“能为开发人员提供足够的指导和限制”的程度。 分而治之的两种方式 分而治之的缘由:问题太复杂了,超出了人们能够“一蹴而就”的范围。(接口和实现分离) 一、先不把问题研究得那么深,那么细,浅尝辄止,见好就收。这种分而治之的方式称为“按问题深度分而治之”。 二、先不研究整个问题,而是研究问题的一部分,分割问题。这种分而治之的方式称为“按问题广度分而治之”。(比如展现层、业务层和数据层的开发)40
  • 41. 随着软件设计工作越来越复杂,将架构设计和详细设计分离已成为普遍的做法。 将设计分为架构设计和详细设计,是对“按问题深度分而治之”思想的运用。 所谓架构设计,就是关于如何构建软件的一些最重要的设计决策; 详细设计针对每个部分的内部进行设计。 可以这么说,软件架构设计应当解决的是全局性的、涉及不同“局部”之间交互的设计问题,而不同“局部”的设计由后续的详细设计负责。41
  • 42. 面对“技术复杂性”和“管理复杂性”这样的双重困难,以架构为中心的开发方法是有效的途径: 一方面:“软件系统的架构涵盖了整个系统,尽管架构的有些部分可能只有‘一寸深’”。构造一个具有一定抽象层次的解决方案,而不是将所有细节统统展开,从而有效地控制了“技术复杂性”。没有“把问题研究那么深、那么细”,属于“按问题深度分而治之” 42
  • 43. 另一方面,因为“架构中包含了关于各元素应如何彼此相关的信息”,从而可以把不同模块分配给不同小组分头开发,而软件架构设计方案在这些小组中间扮演“桥梁”和“合作契约”的作用。每个小组的工作覆盖了“整个问题的一部分”,各小组之间可以互相独立地进行并行工作,这种“分割问题,各个击破”的策略,属于“按问题广度分而治之”。 这样一来,模块的技术细节被局部化到了小组内部,内部的细节不会成为小组间协作沟通的主要内容,理顺了沟通的层次。另外,对“人尽其才”也有好处,不同小组的成员需要精通的技术各不相同。例如,用户界面层、数据管理层、系统交互层 。 43
  • 44. 架构要进行到什么程度 软件架构是团队开发的基础,应明确规定后期分头开发所必须的公共设计约定,为分头开发提供足够的指导和限制。 另一方面,具体的架构设计程度还会因软件项目的不同而不同。 架构设计对软件的不同部分的设计程度并不是整齐划一的。(通信机制、持久化机制、消息机制等对应的公共模块;具体的业务功能模块在架构设计中往往设计程度不深。) 归纳: 由于项目的不同、开发团队情况的不同,软件架构的设计程度会有不同; 软件架构应当为开发人员提供足够的指导和限制。44
  • 45. 高来高去式架构设计的症状 不能为开发人员提供足够的指导和限制的那种架构设计方案。高来高去式架构设计现象极为普遍,危害: 缺少来自架构的足够的指导和限制,开发人员在进入分头开发阶段之后会碰到很多问题,并且容易造成管理混乱,沟通和协作效率低; 对软件质量非常关键的全局性设计决定被拖延到分头开发期间草率决定; 没有完成化解重大技术风险的职责; 团队成员对架构师意见大,团队士气受到打击。45
  • 46. 症状一:缺失重要架构视图。给团队的不同角色提供指导。 症状二:架构设计方案停留在概念性架构的层面,全局性的设计决策最后由具体开发人员从局部视角考虑并确定下来,造成技术和管理上的种种问题。 症状三:名不副实的分层架构。对各层之间交互接口和交互机制的设计严重不足。46
  • 47. 47架构设计的GRASP模式
  • 48. 48
  • 49. 49
  • 50. 50
  • 51. 51
  • 52. 52
  • 53. 53
  • 54. 54
  • 55. 55
  • 56. 56
  • 57. 57
  • 58. 58
  • 59. 59
  • 60. 60
  • 61. 61
  • 62. 62
  • 63. 63
  • 64. 64
  • 65. 65
  • 66. 66
  • 67. (本页无文本内容)
  • 68. (本页无文本内容)
  • 69. 质量属性驱动架构设计策略
  • 70. 软件质量及质量模型 软件质量是一个复杂的概念,不同的人从不同的角度来看软件质量问题,会有不同的理解。 从用户的角度看,质量就是满足客户的需求; 从开发者的角度看,质量 就是与需求说明保持一致; 从产品的角度看,质量就是 产品的内在特点; 从价值的角度看,质量就是客户是否 愿意购买。
  • 71. 在软件项目开发过程中,项目经理眼中的质量就是能“令人满意”地工作以完成预期功能的软件产品。 所谓“令 人满意”,包括功能、性能、接口需求及其他指标,如可靠性、可维护性、可复用性和正确性。 然而在实际工作中,一旦出现问题时,项目管理人员必须权衡利弊,作出取舍,在满足某一个指标的同时,牺牲另外一个或几个指标。比如为了按期交货,需对软件功能进行分类,在第  一个版本中实现优先级较高的功能,在第二个版本中实 现优先级较低的功能。因此,项目经理需要一个对其工作有指导意义的质量模型和度量方法。该模型一方面可以帮助项目经理生产出符合标准的软件产品,另一方面帮助项目经理识别可能影响产品质量的风险。
  • 72. 为什么那么多软件产品需要重新设计? 难以维护? 运行速度太慢? 稳定性差? 无法进行功能扩展? 易遭受安全攻击? 忽视包括质量属性需求在内的非功能需求是致命的。
  • 73. 质量属性分为: 运行期质量属性 开发期质量属性 各类需求架构设计的不同影响 高可移植性 对硬件和平台相关特性进行封装、 隔离 高性能 精心规划职责模型 在质量属性方面需求折衷
  • 74. 质量属性分析的位置 质量属性分析是概念性架构设计的重要步骤。 通过质量属性分析,制定出满足非功能需求的高层设计决策。 质量属性分析所处的位置 如图所示
  • 75. ”属性-场景-决策”表法 运用“关键需求决定架构”的策略,使质量属性分析的“输入”集中到关键质量属性需求。 “属性-场景-决策”表方法提倡通过一组具体场景将要达到的质量属性需求目标细化,再根据场景制定架构决策。
  • 76. “属性-场景-决策“表法
  • 77. (本页无文本内容)
  • 78. “属性-场景-决策”表法的好处: 可操作性强。质量熟悉需求是笼统的目标,而一组质量场景使之明确化。 避免过度设计。借助“属性-场景-决策”表对质量场景进行评估,通过权衡场景发生的概率和遗漏它的代价,决定是否应满足该场景的要求。 便于系统升级时参考。
  • 79. 例:可扩展性质量属性
  • 80. 运用“属性-场景-决策”表方法,细化PM Tool的可扩展性需求,制定架构决策,如下表所示:
  • 81. 非功能需求对软件架构的影响比功能需求更大。 “属性-场景-决策”表可以帮助软件架构师以一种更透明、更可操作的方式完成从质量属性需求到质量场景的细化,最终根据具体的场景有针对性地设计架构决策。
  • 82. (本页无文本内容)
  • 83. 架构的目标正确性correctness 可靠性(Reliable): 软件系统对于用户的商业经营和管理来说极为重要,因此软件系统必须非常可靠。 健壮性robustness 安全性(Secure) : 软件系统所承担的交易的商业价值极高,系统的安全性非常重要。 可伸缩性(Scalable) : 软件必须能够在用户的使用率、用户的数目增加很快的情况下,保持合理的性能。只有这样,才能适应用户的市场扩展得可能性。
  • 84. 可定制化(Customizable) : 同样的一套软件,可以根据客户群的不同和市场需求的变化进行调整。 可扩展性(Extensible): 在新技术出现的时候,一个软件系统应当允许导入新技术,从而对现有系统进行功能和性能的扩展 可复用性reusability 可维护性(Maintainable): 软件系统的维护包括两方面,一是排除现有的错误,二是将新的软件需求反映到现有系统中去。一个易于维护的系统可以有效地降低技术支持的花费。
  • 85. 兼容性compatibility 可移植性portability 易用性ease of use 高效性efficiency timeliness,economy and functionality
  • 86. 客户体验(Customer Experience): 软件系统必须易于使用。 市场时机(Time to Market): 软件用户要面临同业竞争,软件提供商也要面临同业竞争。以最快的速度争夺市场先机非常重要。
  • 87. 87软件可维护性
  • 88. 重型和轻型方法 重型方法:偏重于计划、过程和中间产物 敏捷方法:更加看重人和沟通。人和沟通永远是第一位的,而计划、过程和中间产物,只是保证沟通、实现目标的手段。并非计划、过程、中间产物不重要,但不能本末倒置。 评判软件成功的标准: 很多。对敏捷方法:首先在于交付可用的软件。 为了保证软件的可用性,需求是根本。对于架构设计工作,从需求出发来设计架构,是保证软件可用性的基本的保证。
  • 89. 如何开始架构设计工作 考虑的平台、语言、开发环境、数据库等 误区:架构设计就是写一些空话,套话。 误区:对与客户具体情况密切相关的问题却未系统考虑。 IT界的技术层出不穷,面对着如此之多的技术、平台、框架、函数库,我们如何选择一组适合软件的技术? 要针对需求设计架构。 每个客户都有自身特点,如何才能够设计出符合客户利益的架构? 软件中往往都充斥着众多的问题,在一开始就把所有的问题都想清楚往往很难做到,但是如果不解决问题,风险又居高不下。
  • 90. 架构设计就是铺设软件的主管道。根据什么来制定主管道的粗细、路径等因素?是根据城市的人口、地理位置、水源等因素。对应到软件设计,城市的各因素就是软件中的各种需求:功能需求、非功能需求、变化案例等。 例:城市中自来水管的架设是一项非常的复杂的工程。为了需要满足每家每户的需要,自来水管组成了一个庞大的网络。在这样一个复杂的网络中,如何完成铺设的任务呢。一般的做法是,先找出问题的根源,也就是水的源头。从水源铺设一条管道通至城市,然后根据城市的区域划分,设计出主管道,剩下的就是使用的问题了,每家每户的管道最终都是连到主管道上的。因此,虽然自来水网络庞大复杂。但是真正的主管道是比较简单的。
  • 91. 一般来说,功能需求决定业务架构、非功能需求决定技术架构,变化案例决定架构的范围。 功能需求决定软件能够做些什么。我们需要根据业务上的需求来设计业务架构,以使得未来的软件能够满足客户的需要。 非功能需求定义了性能、效率上的一些约束、规则。而我们的技术架构要能够满足这些约束和规则。变化案例是对未来可能发生的变化的一个估计,结合功能需求和非功能需求,我们就可以确定一个需求的范围,进而确定一个架构的范围。 例:一个字处理软件,功能需求可以简单概括为格式化用户输入的文字,非功能需求可能是格式化大小为1000K的一段文字的处理速度不能低于10S,变化案例可能是推出多种语言版本。则在设计业务架构时,我们会集中于如何表示文字、图象、媒体等要素,此外需要有另外的技术架构来处理速度问题,比如缓冲技术,对于变化案例,我们也要考虑相应的架构,比如把字体独立于程序包的设计。
  • 92. 架构设计的两项工作 分析:分析是分析需求 设计:设计软件的大致结构。 很多方法论分离二者,其实无一定的界限,做分析的时候会想到如何设计,而思考如何设计反过来又会影响分析的效果。可以说,两者间是相互联系和不断迭代的。
  • 93. 需求与架构都应迭代进行 一点一点的作需求。这种做法在那些需求变化快的项目中尤其适用。 由于我们采用的流程是一种迭代式的流程,这里我们将会面临着如何对待上一次迭代的中间产物的问题。如果我们每一次迭代都需要修改已存在的中间产物,那么这种维护的成本未免过大。因此,敏捷方法论的基本做法是,扔掉那些已经没有用处的中间产物。 软件要比文档重要。生成中间产物的目的都是为了生成最终的程序,对于这些已经完成作用的模型,没有必要付出额外的维护成本。
  • 94. 架构设计中的一些提示(也是抛弃模型的必要条件): 简单化:简单的模型和简单的程序。模型和程序越复杂,就需要更多的精力来处理它们。因此尽可能简化它们,为的是更容易的处理它们。 高效沟通渠道:通过增强沟通的效果来减少对中间产物的需要。试想若随时能从客户处得到需求的细节资料,则前期需求调研就没有必要做得太细。 角色交叉轮换:开发人员间建立起交换角色的机制,能够尽量的避免各子系统诸侯割据的局面。 清晰的流程:或明确的过程。过程在方法论中是重点,敏捷方法论也不例外。开发人员能够清楚的知道,今天做什么,明天做什么。过程不是给别人看的,而是给自己用的。
  • 95. 工具:好用的工具能够节省大量的时间,工具并不仅指CASE工具,还包括版本控制工具、自动测试工具、画图工具、文档制作和管理工具。使用工具要注意成本和效益的问题。 标准和风格:语言标准不同是沟通的很大障碍。语言从某个角度来看属于一种标准、一种风格。因此,一个团队如果采用同样的编码标准、文档标准、注释风格、制图风格,那么这个团队的沟通效率一定非常高。 如果上述的环境都不具备,或是欠缺好几项,那你的文档的模型还是留着的好。
  • 96. 仅针对需求设计架构 不要做未来才有用的事情。 有时我们会把架构考虑得非常复杂,主要原因是把很多未来的因素放入到现在来考虑。 或在开发第一个产品的时候就试图做成完美的框架。 以上的这两种思路有没有错呢?没有错,这只是如何看待投入的问题,有人希望开始的时候多投入一些,这样后续的投入就会节省下来。但在现实中,由于需求的不确定性,希望通过增加开始阶段的投入来将降低未来的投入往往是难以做到的,框架的设计也绝非一蹴而就的,因此这种做法并不好。 同其它很多事物一样,架构设计应保持简单性和迭代过程!
  • 97. 引入模式 模式帮助我们抓住重点。 为了解决设计文档编辑器引出的七个问题,一共使用了8种不同的模式。这8种模式的组合其实就是架构,因为它们解决的,都是系统中最高层的问题。 架构也是存在模式的。比如,对于系统结构设计,我们使用层模式;对于分布式系统,我们使用代理模式;对于交互系统,我们使用MVC(模型-视图-控制器)模式。模式本来就是针对特定问题的解,因此,针对需求的特点,我们也可以采用相应的模式来设计架构。
  • 98. (本页无文本内容)
  • 99. PetShot案例 在MVC图背后隐藏着的需求: 系统需要支持多种用户界面,包括为普通用户提供的HTML界面,为无线用户提供的WML界面,为管理员提供的Swing界面,以及为B2B业务设计的WebService界面。这是系统最重要的需求,因此,系统的设计者就需要确定一个稳定的架构,以解决多界面的问题。 相对于多界面的问题,后端的业务处理逻辑都是一致的。比如HTML界面和WML界面的功能并没有太大的差别。把处理逻辑和界面分离开来还有额外的好处,可以在添加功能的同时,不涉及界面的改动,反之亦然。(耦合度的问题)。 MVC模式正可以适用于解决该问题。系统使用控制器来为业务逻辑选择不同的界面,这就完成了MVC架构的设计思路。在架构设计的工作中,我们手头上有模式这样一张好牌,有什么理由不去使用它呢?
  • 100. (本页无文本内容)
  • 101. 抓住重点 架构是一种抽象,即架构设计摒弃了具体的细节,仅仅抓住软件最高层的概念,也就是最上层、优先级最高、风险最大的那部分需求。 考虑、分析、解决一个问题,一定有一个渐进的过程。架构设计就是解决问题其中比较早期的一个阶段,我们不会在架构设计这个阶段投入过多的时间,因此关键点在于我们要能够在架构设计中把握住需求的重点。 比如,分布式系统和交互系统,分布和交互就是这两个系统的重点。那么,如果说我们面对的是一个分布式的交互系统,那么,我们就需要把这两种特性做为重点来考虑,并以此为基础,设计架构。而宠物商店的范例也是类似的,除了MVC的架构,还有很多的设计问题需要解决,例如用于数据库访问的数据对象,用于视图管理的前端控制器等。但是这些相对于MVC模式来说,属于局部的,优先级较低的部分,可以在架构确定后再来设计。
  • 102. 架构设计和领域专家 一个架构要设计的好,和对需求的理解是分不开的。因此在现实中,业务领域专家凭借着他对业务领域的了解,能够帮助开发人员设计出优秀的架构来。 架构是需要抽象的,它是现实社会活动的一个基本模型,而业务领域的模型仅仅凭开发人员是很难设计出来的。在ERP的发展史上,MRP发展为MRPII,再发展到闭环MRP,直到发展成为现在的ERP,主要的因素是管理思想的演化,也就是说,对业务领域的理解进步了,架构才有可能进步。 因此,敏捷型架构设计的过程中,我们也非常强调领域专家的作用。
  • 103. 软件约束条件与架构的影响 资金 时间 业务 运行环境 开发团队 实现技术等
  • 104. 104领域模型设计领域模型(Domain Model) 商业建模范畴的概念: 同行业企业的业务模型必定有很大的共性和内在的规律性,由这个行业内的各个企业的业务模型再向上抽象出来整个行业的业务模型,即领域模型。 技术建模范畴的概念:用编程语言来实现商业领域模型。
  • 105. 关系: 对行业经验积累不足的软件公司,开发软件由需求驱动,而非商业的领域模型驱动。 商业领域模型与编程语言的不是一对一的对应关系。例如用EJB2模型,需要最少两个以上的EJB,即一个 Session Bean,处理面向流程的控制逻辑,一个Entity Bean,处理面向持久化的实体逻辑(持久化操作附着在Entity Bean的Home接口上)。如果是更加复杂的领域模型,则需要更多的EJB,一般一个领域模型需要多个Entity Bean和多个Session Bean。 使用基于POJO模型的实现,则粗颗粒度的EJB要继续细分:一个Entity Bean对应三个以上POJO:一个或者多个实体类,DAO接口、DAO接口实现类;一个Session Bean要切分为多个业务Bean。105
  • 106. 106层次结构 领域模型 从EJB到轻量级框架
  • 107. 107层次结构表现层(present) 业务层 业务层外观 业务服务层 领域对象管理/服务/仓库层 领域对象层 持久层 数据访问层 数据库
  • 108. 108领域模型中的各种角色: 实体--有唯一的标识,并且要有属性和行为(非GET/SET),添加了行为,使其具有生命力。往往在设计时,实体的形为最难决断。为确定行为,我们必须识别它们的责任和协作。类的责任是指该类要做、知道、或决定的一切,由一个或多个方法完成。类中有属性和关联,协作就是为完成自己的责任所调用其它关联类。 值对象--没有标识没有行为。如Address类。 工厂---定义创建实体的方法,封装实例化对象并将一些关联对象注入。 仓库(repository)管理实体的集合,主要有查找和删除实体的方法.实现类可以调用执久化层(如Hibernate,Ibatis) 服务(Service) ,实现整个应用程序的工作流(workflow)。服务包含那些无法指派的单个实体的行为,由作用于多个对象方法组成。如可以调用repository查找到实体对象,然后委派给这些对象。服务和facade很像,但不一样,它不处理以下事情:1)执行事务。2)收集返回给表现层的数据。3)脱钩对象。4)其它事情。服务可以说是业务的协调者,业务逻辑可以分散到实体对象中。
  • 109. 109
  • 110. 2、层次之间的交互 A、页面提交表单数据到Action,Action创建DTO对象并设置相应属性值为表单数据 B、Action传递DTO对象给Facade C、Facade中套用ServiceTemplate事务模板以加入事务管理,在ServiceTemplate中根据具体业务调用Factory或Reposistory,分别create或者load出DomainModel对象 D、Facade传递DomainModel对象给Service E、Service执行具体业务逻辑(调用DomainModel对象相应的业务方法) F、Service调用Reposistory对状态已改变的DomainModel对象进行持久化操作(调用相应DAO) 110
  • 111. 注: 在Facade或Service中如果需要查询DomainModel对象中的属性值,调用DomainModel对象的getDataInfo()方法得到DataInfo对象,通过DataInfo对象查询所需数据,包括Service返回给Façade业务处理结果中所包含的业务数据。 111
  • 112. 112领域模型失血模型 贫血模型 充血模型 胀血模型
  • 113. 113失血模型DO只有属性及其getter/setter方法,没有任何业务逻辑。 缺点:行为与数据分离,很多情况导致维护与理解困难。
  • 114. 114贫血模型DO包含不依赖于持久化的领域逻辑;依赖持久化的领域逻辑归入Service层。 Service(业务逻辑,事务封装) DAO DO 优点: 各层单向依赖,结构清楚,易于实现和维护。 设计简单易行,底层模型非常稳定。 缺点: DO部分的持久化逻辑被放入Service层,不够OO。 Service层过重。
  • 115. 115充血模型与贫血模型类似,不同处在于如何划分业务逻辑:绝大多业务逻辑都应该放在DO里(包括持久化逻辑),而Service层很薄,仅仅封装事务和少量逻辑,不和DAO层打交道。 Service(事务封装) DO DAO 优点: 符合OO Service层很薄,只充当Facade的角色,不和DAO打交道。
  • 116. 116缺点: DAO和DO双向依赖。 如何划分Service层逻辑和Domain层逻辑没有确定的规则,取决与设计人员自己的理解。 Service层封装事务,须对所有的DO逻辑提供相应的事务封装方法,造成重定义所有的Domain logic。 Service的事务化封装的意义等于把OO的Domain logic转换为过程的Service 事务脚本。充血模型在domain层实现的OO在Service层又变成了面向过程。
  • 117. 117胀血模型取消Service层,只剩下DO和DAO层,在DO的domain logic上面封装事务。 DO(事务封装,业务逻辑) DAO (RoR甚至合并为一层) 优点: 分层简化 符合OO 缺点: service逻辑也强行放入DO ,引起了DO不稳定。 DO暴露给web层过多的信息,可能引起不必要的耦合。
  • 118. 118原则: 业务对象封装了内在的业务逻辑,而应用服务封装了外在于业务对象的业务逻辑。
  • 119. 119EJB到轻量级框架EJB POJO (业务逻辑) + 轻量级框架([Hibernate、JDO、iBATIS](持久化)、Spring(事务管理、安全))
  • 120. 120EJBEJB: 编写分布式业务应用程序的Java标准架构。 提供大量服务: 声明型事务, EIB容器自动启动、提交和回滚事务。 业务逻辑能参与由远程客户发起的分布式事务。 提供声明型安全,大部分情况下不再摇要编写安全代码求(bean部署描述符里的条目指定准可以防问某个具体bean)。
  • 121. 121例:在两个账号间进行转账的服务。
  • 122. 122
  • 123. 123新设计是面向对象、基于POJO, 而非基于EJB的过程式。 它使用构建在JDBC上的持久层框架来访问数据库, 并不直接使用JDBC。 业务逻辑由POJO facade而非会话bean进行封装。 事务由Spring框架而非EJB容器进行管理。 业务逻辑向表现层返回实际的业务对象,而不是DTO。 应用程序通过将组件的依赖作为setter或构造子参数传入来进行组装,而不是之前采用Java命名和目录接口(JNDI )查询的组件。 由于该设计面向对象,并采用上述轻量级技术,因此较之前看到的EJB版本对开发人员要友好。
  • 124. 124基于轻量级框架设计的好处是,它提供事务和持久化时并不要求应用程序类实现任何特殊接口。甚至当应用程序的类需要运行在事务里或是持久的时候,它们仍是POJO,设计者可以继续享受POJO的种种好处。
  • 125. 125
  • 126. 126面向对象的优点: 整个设计更易理解和维护。它并不是一个无所不能的大型类, 而是由大量小类组成, 每个类只共有若干职责。此外,诸如Account、BankingTransaction和OverdraftPolicy类都与现实世界的概念对应, 因此易于理解。 其次,面向对象设汁也更易测试: 所有类都能并且应当进行独立的测试。EJB只能通过调用它的public 方法如Transter进行测试,难度大。(只能测试p-blic方法暴露的复杂功能,无法测试其中简单的部分)。 面向对象象设计更易扩展, 它可使用设计模式,如Stategy和Template等。EJB风格的过程式设计往往需耍修改核心代码。
  • 127. 127不适合用面向对象的场合: 大量数据集合的关系操作。 以数据库为中心的管理程序 :这个领域所对应的现实世界是一个面向关系的世界,表与表的关联体现的是彼此的业务关系。 复杂的SQL固然不好维护,但业务真是复杂到简单的SQL都难以描述的程度,采用面向对象描述则更加困难,维护也更困难,同时还损失了效率。 比较大的事务。 性能要求高的地方。(直接用Sql或者存储过程;牺牲可维护性和可复用性) 上层流程。
  • 128. 128消除DTO
  • 129. 129部署POJO程序
  • 130. 130
  • 131. 131
  • 132. 软件架构模式分析及其实际运用
  • 133. 软件架构 软件架构概论 架构的目标 架构的种类 软件框架 常见的架构模式
  • 134. 软件架构概论系统架构是一个软件系统从整体到部分的最高层次的划分。 一个系统通常是由元件组成的,而这些元件如何形成、相互之间如何发生作用,则是关于这个系统本身结构的重要信息。 详细地说,就是要包括架构元件(Architecture Component)、联结器(Connector)、任务流(Task-flow)。所谓架构元素,也就是组成系统的核心"砖瓦",而联结器则描述这些元件之间通讯的路径、通讯的机制、通讯的预期结果,任务流则描述系统如何使用这些元件和联结器完成某一项需求。
  • 135. 建造一个系统所作出的最高层次的、以后难以更改的,商业的和技术的决定。 在建造一个系统之前会有很多的重要决定需要事先作出,而一旦系统开始进行详细设计甚至建造,这些决定就很难更改甚至无法更改。显然,这样的决定必定是有关系统设计成败的最重要决定,必须经过慎重的研究和考察。
  • 136. 架构的种类根据我们关注的角度不同,可以将架构分成三种: 逻辑架构 物理架构 系统架构
  • 137. 逻辑架构软件系统中元件之间的关系,比如用户界面,数据库,外部系统接口,商业逻辑元件等等。
  • 138. 物理架构软件元件是怎样放到硬件上的 下图描述了一个分布于北京和上海的分布式系统的物理架构,图中所有的元件都是物理设备,包括网络分流器、代理服务器、WEB服务器、应用服务器、报表服务器、整合服务器、存储服务器、主机等等。
  • 139. 系统架构系统的非功能性特征,如可扩展性、可靠性、强壮性、灵活性、性能等。 系统架构的设计要求架构师具备软件和硬件的功能和性能的过硬知识,这一工作是架构设计工作中最困难的工作。
  • 140. 架构的两要素元件划分和设计决定。 逻辑元件: 一个软件系统中的元件首先是逻辑元件。这些逻辑元件如何放到硬件上,以及这些元件如何为整个系统的可扩展性、可靠性、强壮性、灵活性、性能等做出贡献,是非常重要的信息。 设计决定: 进行软件设计需要做出的决定中,必然会包括逻辑结构、物理结构,以及它们如何影响到系统的所有非功能性特征。这些决定中会有很多是一旦作出,就很难更改的。 基于数据库的系统架构: 一般有多少个数据表,就会有多少页的架构设计文档。比如一个中等的数据库应用系统通常含有一百个左右的数据表,这样的一个系统设计通常需要有一百页左右的架构设计文档。
  • 141. 从概念性架构到实际架构 就是多 (Less is more.)。 -- 密斯·凡德罗 概念性架构是对系统设计的最初构想。 一般来说,实际的软件架构设计过程是,先进行概念性架构的设计,把最关键的设计要素和交互机制确定下来,然后再考虑具体技术的运用,设计出实际架构。
  • 142. 架构设计中的关键要素及解决策略 策略是制胜的关键。-- 张明正,《挡不住的趋势》 最好的软件开发人员都知道一个秘密:美的东西比丑的东西创建起来更廉价,也更快捷。-- Robert C. Martin, 《软件之美》 时间就是系统架构的生命。-- Philippe Kruchten 方法产生于恐惧。 面对时间紧迫的压力,我们有理由质疑那种不顾时间花销、一味追求软件架构高质量的做法。软件架构是软件系统质量的核心,必须足够重视,但在不适当的时候“用时间换完美”会毁掉整个项目。 架构设计并非“好的就是成功的”,而是“适合的才是成功的”。
  • 143. 方法论
  • 144.  Alistair Cockburn 的七条定理,总结为:沟通和反馈。 RUP、XP 重量之争
  • 145. 软件架构设计中的关键要素及解决策略:              关键要素                         策略 ----------------------------------------------- ----------------------- 1. 是否遗漏了至关重要的非功能需求?      全面认识需求。 2. 能否驯服数量巨大且频繁变化的需求?   关键需求决定架构。 3. 能否从容地设计软件架构的不同方面?   多视图探寻架构。 4. 是否及早验证架构方案并作出了调整?   及早验证架构。
  • 146. 软件架构设计过程总览 一般的软件过程:
  • 147. 需求分析的几个概念 需求捕获 vs 需求分析 vs 系统分析 需求捕获是获取知识的过程,知识从无到有。 需求分析是挖掘和整理知识的过程,它在已掌握知识的基础上进行。 系统分析?如果说需求分析致力于“做什么”,那么系统分析则涉及“怎么做”。 软件架构师不必是需求捕获专家,也不必是编写《软件需求规格说明书》的专家。但他一定应在需求分类、需求折衷和需求变更的研究方面是专家,否则他和其他软件架构师相比,就输在了“起跑线”上。
  • 148. 概念性架构设计 概念性架构设计的迭代方式: 1. 鲁棒性分析; 2. 引入架构模式; 3. 质量属性分析。
  • 149. 鲁棒性分析 所谓鲁棒性分析是这样一种方法:通过分析用例规约中的事件流,识别出实现用例规定的功能所需的主要对象及其职责,形成以职责模型为主的初步设计。 鲁棒性分析是从功能需求向设计方案过度的第一步,所获得的初步设计是一种理想化的职责模型,它的重点是识别组成软件系统的高级职责块、规划它们之间的关系。 鲁棒性分析填补了分析和设计之间的鸿沟。 鲁棒图包含三种元素:边界对象、控制对象和实体对象。
  • 150. 引入架构模式 较为经典的几种架构模式: 分层、MVC、微内核、基于元模型的架构、管道-过滤器等。
  • 151. 质量属性分析 “属性-场景-决策”表方法:
  • 152. 细化架构设计 架构细化工作主要体现在基于五视图方法进行架构细化:                       约束                         ↓                ┌───────┐    领域模型 -> │基于五视图方法│    关键需求 -> │              │-> 架构方案    概念架构 -> │ 进行架构细化 │                └───────┘                        ↑                       经验
  • 153. (本页无文本内容)
  • 154. 逻辑架构设计中,“发现通用机制”是应被特别强调的。 机制(Mechanism)是模式的实例。机制是特定上下文中重复出现的问题的特定解决方案。 具有良好架构的系统具备概念完整性。它通过对系统架构建立一种清晰的认识来发现通用的抽象和机制。利用这种共性使最终产生的系统结构更为简单。
  • 155. 实现并验证软件架构 好的策略必须是一再求证、测试、发现瑕疵漏洞,另想途径或方法来弥补策略不足,有时甚至得全盘放弃,重新策划。-- 张明正,《挡不住的趋势》 架构原型对功能性需求的实现非常有限,那么“架构验证”要验证什么? 答案是要验证架构对质量属性需求的支持程度,包括运行期质量属性和开发期质量属性。
  • 156. 验证架构的两种方法: 原型法。 对于项目型开发,常采用“原型法”。即对一组架构设计决策在非功能需求方面的满足程度进行验证。该原型往往是演进型,而非抛弃型。 框架法。 对于产品型开发,采用“框架法”有更多优点。该方法将架构设计方案用框架的形式实现,并在此基础上进行评估验证。在框架实现后,在框架基础上实现部分应用的功能,即实现一个小的垂直原型,从而进行实际非功能测试和开发期质量属性评价。
  • 157. 软件框架什么是框架 框架与架构的区别 常见的框架
  • 158. 为什么要用框架因为软件系统发展到今天已经很复杂了,特别是服务器端软件,设计到的知识,内容,问题太多。在某些方面使用成熟的框架,可以避免重复做已有的基础工作,而只需要集中精力完成系统的业务逻辑设计。 框架一般是成熟,稳健的,可以处理系统很多细节问题,比如,事物理,安全性,数据流控制等问题。 框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,使用框架的开发者可以直接享受别人升级代码带来的好处。 框架一般处在低层应用平台(如J2EE)和高层业务逻辑之间的中间层。
  • 159. 159常见的JAVA框架EJB Struts Spring Hibernate IBatis JSF tapestry WAF Turbine COCOON ECHO JATO TCF Ext …
  • 160. 160.NET 框架.NET 框架是创建、部署和运行 Web 服务及其他应用程序的一个环境。它包括三个主要部分:公共语言运行时、框架类和 ASP.NET。 .NET 框架对 Web 站点的支持: ASP.NET 在编写 Windows 软件(使用 ATL/COM、MFC、VB或标准 Win32)方面,与当前创建应用程序的方式相比.NET都具有的优势。
  • 161. C++框架标准库 Dinkumware C++ Library、RogueWave Standard C++ Library 、SGI STL 、STLport GUI MFC、QT 、 WxWindows 、Fox 、WTL 、GTK、 BCG、 Xtreme Toolkit 网络通信 ACE 、StreamModule、SimpleSocket 综合 P::Classes、 ACDK - Artefaktur Component D evelopment Kit 、 dlib C++ library、 Chilkat C++ Libraries 、C++ Portable Types Library (P Types) 、LFC 其他库 Loki 、ATL 、FC++: The Functional C++ Libr ary、FACT! 、Crypto++
  • 162. Boost Regex 正则表达式库 Spirit  LL parser framework,用C++代码直接表达EBNF Graph 图组件和算法 Lambda 在调用的地方定义短小匿名的函数对象 concept check 检查泛型编程中的concept Mpl 用模板实现的元编程框架 Thread  可移植的C++多线程库 、 Python 把C++类和函数映射到Python之中 Pool 内存池管理 smart_ptr  智能指针
  • 163. XML Xerces 、 XMLBooster 、Pull Parser 、 Xalan 、 CMarkup 、libxml++ 科学计算 Blitz++ 、POOMA 、MTL 、CGAL 游戏开发 Audio/Video 3D C++ Programming&nbs p;Library 、KlayGE 龚敏敏、 OGRE 线程 C++ Threads 、 ZThreads 序列化 s11n 、Simple XML Persistence Library 字符串 C++ Str Library 、 Common Text Transformation Library 、 GRETA
  • 164. 164不同层次的模式架构模式(Architectural Pattern) 设计模式(Design Pattern) 代码模式(Coding Pattern)
  • 165. 区别:在于三种不同的模式存在于它们各自的抽象层次和具体层次。 架构模式是一个系统的高层次策略,涉及到大尺度的组件以及整体性质。架构模式的好坏可以影响到总体布局和框架性结构。 设计模式是中等尺度的结构策略。这些中等尺度的结构实现了一些大尺度组件的行为和它们之间的关系。模式的好坏不会影响到系统的总体布局和总体框架。设计模式定义出子系统或组件的微观结构。 代码模式是特定的范例和与特定语言有关的编程技巧。代码模式的好坏会影响到一个中等尺度组件的内部、外部的结构或行为的底层细节,但不会影响到一个部件或子系统的中等尺度的结构,更不会影响到系统的总体布局和大尺度框架。
  • 166. 架构模式(Architectural Pattern)软件体系结构通常被称为架构,指可以预制和可重构的软件框架结构。架构尚处在发展期,对于其定义,学术界尚未形成一个统一的意见,而不同角度的视点也会造成软件体系结构的不同理解。 众多软件架构概念都是围绕“组成”和“决策”两个视角展开的。
  • 167. Booch、Rumbaugh和Jacobson的定义 架构是一系列重要决策的集合,这些决策与以下内容有关:软件的组织,构成系统的结构元素及其接口的选择,这些元素在相互协作中明确表现出的行为,这些结构元素和行为元素进一步组合所构成的更大规模的子系统,以及指导这一组织——包括这些元素及其接口、它们的协作和它们的组合——架构风格。 IEEE 610.12-1990软件工程标准: 架构是以组件、组件之间的关系、组件与环境之间的关系为内容的某一系统的基本组织结构,以及指导上述内容设计与演化的原理(Principle)。
  • 168. Garlan的定义 架构包括组件(Component)、连接件(Connector)和约束(Constrain)三大要素。 组件可以是一组代码(例如程序模块),也可以是独立的程序(例如数据库服务器)。 连接件可以是过程调用、管道和消息等,用于表示组件之间的相互关系。 “约束” 一般为对象连接时的规则,或指明构件连接的形式和条件,例如,上层构件可要求下层构件的服务,反之不行;两对象不得递规地发送消息;代码复制迁移的一致性约束;什么条件下此种连接无效等。 Shaw的定义: “软件系统的架构将系统描述为计算组件及组件之间的交互”。 架构=组件+交互
  • 169. 几种典型的架构模式系统软件 分层(Layer) 管道和过滤器(Pipes and Filters) 黑板(Blackboard) 开发分布式软件 经纪人(Broker) 客户/服务器(Client/Server) 点对点(Peer to Peer) 交互软件 模型-视图-控制器(Model-View-Controller) 显示-抽象-控制(Presentation-Abstraction-COntrol)
  • 170. 其它面向对象风格(ADT) 基于消息广播且面向图形用户界面的Chiron2风格 基于事件的隐式调用风格(Event-based, Implicit Invocation) …
  • 171. 分层(Layer)从不同的层次来观察系统,处理不同层次问题的对象被封装到不同的层中。 软件为什么要分层? 为了实现“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源… 面向对象的、基于模块化的组件设计需要能够方便地修改应用程序的各个部分。完成这一目标的一种好方法就是在层上工作,将一个应用程序的主要功能分离到不同的层或者级中。
  • 172. 分层模型从本质上讲,层代表了一个应用程序主要的功能。一般地,我们将应用程序功能分为三个方面,对应3层架构模式。它们是数据层(data layer)、商务层(business layer)和表示层(presentation layer)。 数据层:包含数据存储和与它交互的组件或服务。这些组件和服务在功能上和中间层相互独立(尽管在物理上不必一定相互独立--它们可以在同一台服务器上)。 中间层:包括一个或者多个组件服务,它们应用商务规则、实现应用程序逻辑并完成应用程序运行所需要的数据处理。作为这个过程的一部分,中间层负责处理来自数据存储或者发送给数据存储的数据。 表示层:从中间层获得信息并显示给用户。该层同时也负责和用户进行交互,比较返回的信息并将信息回送给中间层进行处理。
  • 173. 数据层从数据库中获得较为原始的数据,商务层把数据转换成符合商务规则的有意义的信息,表示层把信息转换成对于用户有意义的内容。 这种分层设计方式很有用,因为每一层都可以独立地修改。我们可以修改商务层,不断地从数据层接受相同的数据,并把这些数据传递到表示层,而不用担心出现歧义。我们也可以修改表示层,使得对于站点外观的修改不必改动下面的商务层逻辑。
  • 174. 管道和过滤器(Pipes and Filters)管道和过滤器架构模式是为处理数据流的系统提供的一种模式。它是由过滤器和管道组成的.每个处理步骤都被封装在一个过滤器组件中,数据通过相邻过滤器之间的管道进行传输。每个过滤器可以单独修改,功能单一,并且它们之间的顺序可以进行配置。
  • 175. 一个著名的例子是传统的编译器。传统的编译器一直被认为是一种管道系统,在该系统中,一个阶段(包括词法分析、语法分析、语义分析和代码生成)的输出是另一个阶段的输入。
  • 176. 问题: 一个必须处理或转换输入数据流的系统。把这样的系统作为单个组件实现是不容易的: 系统必须由几个开发人员同时进行协作开发,整个系统任务自然就被分解为几个处理阶段,而且需求很容易变动。因此你就要通过替换或重新排序处理步骤来为将来的灵活性作规划。通过加入这样的灵活性,采用现有处理组件构建是可以办到的。 系统的设计尤其是处理步骤的内部连接,必须考虑以下因素: 未来系统的升级通过替换某些处理步骤,或重组步骤。 不同的语境中小的处理步骤要比大的组件更易于重用。 不相连的处理步骤不可共享信息。 存在不同的输入数据源,可以用多种方式输出或存放最终结果。
  • 177. 解决方案与结构管道和过滤器体系架构模式把系统任务分成为几个独立的处理步骤。这些步骤采用通过系统的数据流连接。一个步骤的输出是下一个步骤的输入。每个处理步骤由一个过滤器组件实现,它处理或者转化数据,并且系统的输入可以是多种数据源。 这种体系架构模式具有许多特性: 过滤器是独立运行的部件。也就是除了输入和输出外,每个过滤器不受任何其他过滤器运行的影响。在设计上,过滤器之间不共享任何状态信息。 独立性还表现在它对其处理的上游和下游连接的过滤器是"无知"的.它的设计和使用不对与其连接的任何过滤器施加限制,唯一关心的是其输入数据的,然后进行加工处理,最后产生数据输出。
  • 178. 优点与缺点优点: 结构简单:系统的行为是所有过滤器行为的简单复合。 系统易于维护和增强:增加新过滤器,替换旧过滤器。 支持复用:过滤器只同其输入、输出端口的数据相关。 各过滤器可以并发运行。 缺点: 容易导致批处理方式:每个过滤器从输入数据到输出数据的转换是一个整体。转换通常不适合交互式的应用。 有时必须维护两个分离而又相关的流之间的对应关系。 同实现有关,过滤器之间的数据传输率较低,而且每个过滤器都要作类似的数据打包和解包的工作。
  • 179. 黑板(Blackboard)又称看板模式:在这种架构中,有两种不同的构件:一种是表示当前状态中心数据结构;另一种是一种相互独立的构件,这些构件对中心数据进行操作。这种架构主要用于数据库和人工智能系统的开发。 模式识别、数据挖掘。
  • 180. 经纪人(Broker)客户和服务器通过一个经纪人部件进行通信,经纪人负责协调客户和服务器之间的操作,并且为客户和服务器发送请求和结果信息。
  • 181. 客户/服务器(Client/Server)系统分为客户和服务器,服务器一直处于侦听的状态,客户主动连接服务器,每个服务器可以为多个客户服务。
  • 182. 优缺点优点: 结构简单,系统中不同类型的任务分别由客户和服务器承担,有利于发挥不同机器平台的优势; 支持分布式、并发环境,特别是当客户和服务器之间的关系是多对多时,可以有效地提高资源的利用率和共享程度; 服务器集中管理资源,有利于权限控制和系统安全。 缺点: 在大多数client-server风格的系统中,构件之间的连接通过(远程)过程调用,接近于代码一级,表达能力较弱。
  • 183. 点对点(Peer to Peer)系统中的节点都处于平等的地位,每个节点都可以连接其他节点。在这种架构中,一般需要由一个中心服务器完成发现和管理节点的操作。ICQ以及Web Service技术的大多数应用,都是典型的点对点结构。
  • 184. 模型-视图-控制器(MVC)当应用程序的用户界面非常复杂,且关于用户界面的需求很容易变化时,我们可以把交互类型的软件抽象成模型、视图和控制器这三类组件单元,这种抽象可以很好地分离用户界面和业务逻辑,适应变化的需求。大多数现代交互软件都在一定程度上符合这一架构模型的特点。 MVC模式最吸引人之处在于它迫使用户必须抽象自己的代码,把项目分为表示、逻辑和控制三部分,每部分间的关联较小。 以MVC模式构造软件,可以使得软件结构灵活、重用性好、扩展性佳。
  • 185. 模型—视图—控制器交互的示意图
  • 186. 模型:视图:控制器:模型: 模型表示应用的数据及操作这些数据的逻辑方法。任何和整个应用有关的持久性数据都应该放在模型中。对于模型,它所提供的API不能只针对某一个专门的视图或控制器,应该更一般化以适应不同客户的需求。 视图: 视图将模型的当前状态展示给用户,具体的显示方法由视图负责,因此一个模型可以适用多个不同的视图。在模型状态改变后,通过模型和视图之间的协议,视图得知这种改变并修改自己的显示。对于用户的输入,视图将它们交给控制器处理。 控制器: 控制器负责交互和将用户输入的数据导入模型,它还利用用户的输入将应用转向其他视图。一些非持久的临时数据也应该在视图中存取。
  • 187. 采用MVC的好处显示、逻辑和数据分开,这样一方面的改变不会影响另一方面。 更新视图: 如原来用的是CLI (Command Line Interface)的,后来要改成GUI,只要了解原来的模型和控制器的接口,然后构造GUI,把它按过去的协议和模型关联起来就可以了,这样做增加了组件的重用性和灵活性。 复用视图: 假设针对某个模型数据开发了一套View,那么在其他访问该模型数据的地方,完全可以再次使用该套件或将现在的View组合成一个复合视图。每个单视图有自己和模型的连接协议和自己的响应控制器,这样开发就仅仅变成了简单的组合。 更新控制器: 以在不更改视图显示的情况下,更改控制器,以达到更改视图与用户交互的响应模式的目的。
  • 188. 在Java Web应用中的惯用法模型: 模型的构建方式可以随应用成本的不同而不同。 在两层结构的应用中,Web层直接和持久数据层交互,此时的模型通常是一系列的Java对象。这些对象可能是从关系型数据库中通过查询得到的,或者是通过关系—对象映射框架得到的。 N层体系比较复杂,比如Web层和EJB服务器通信。虽然在EJB2.0规范中,通过本地接口来提高实体Bean问效率,但Web层直接访问实体Bean仍然是效率比较低的,因此在一般情况下, Web层访问有由会话Bean返回的JavaBeans,这被称为值对象模式( Value Obiect) 。
  • 189. Event-based风格优点: 支持复用,构件通过登记它所感兴趣的事件被引入系统; 便于系统演化,构件可以容易地升级或更换。 缺点: 系统行为难以控制,发出事件的构件放弃了对系统的控制,因此不能确定系统中有无或有多少其它构件对该事件感兴趣,系统的行为不能依赖于特定的处理顺序。 同事件关联的会有少量的数据,但有些情况下需要通过共享区传递数据,这时就涉及到全局效率和资源管理问题。
  • 190. 架构设计原则
  • 191. 软件被用来解决人们的业务或领域问题,开 发软件的过程就是去获得解决业务问题结果发软件的过程就是去获得解决业务问题结果 的过程。 人类在问题解决规律研究方面已经有大量的 现成成果;传统的问题解决步骤,实际上为 软件开发(解决软件问题)提供了现成的 (战略级的)理论方法框架。 现代的软件工程技术,其基础实际上就建立 在这些问题解决规律之上。 前段中所描述的问题解决步骤,存在一个隐含的假定前提,就是所有问题都能够采用同 样简单的方法、步骤来加以解决。对于那些普通、单一的问题而言,这个假定是成立的;但对于软件开发这种复杂和多方面问题交织在一起的情况,假定就不再成立了。实际上,软件具有不一致性与多样性,不同性质的软件,不相同的地方很多,所适应的 开发方法也可能完全不同。
  • 192. 破除软件开发中的神秘主义 国内软件界存在一定程度的神秘主义倾向,在自己无力使用常规软件工程途径解决软件问题后,往往简单地将软件开发归于艺术化、玄学化。 经常看到的一种典型现象——某个高手熬了几个通宵,终于拿出了一个精巧的设计方案;团队其他成员都很钦佩他,并想向他学习其中设计的技巧;但高手吹嘘说这完全是靠其灵感所得,思考过程毫无逻辑可言。
  • 193. 光有宏观的过程、方法还不够 无论是简单的问题解决步骤,还是包含了大量实践活动的软件过程域(科目),它们都属于宏观的解决途径。然而,任何软件问题,其最终解决还是要落实到具体的细节之上,这些问题细节需要采用与之相对应的微观方法来解决。 例如,架构设计作为技术解决过程域中的一项实践 (或者分析设计科目中的一项活动),涉及到大量的模块划分问题;而帮助人们进行划分的方法则主要有比较、分类等思维法。
  • 194. (本页无文本内容)
  • 195. 抽象(Abstraction)、封装(Encapsulation)和信息隐藏(Information Hiding) 分而治之(Divide-And-Conquer)和模块化(modularization) liskov替换原则(LSP) 策略和实现的分离(Separation of Policy and Implementation) 接口和实现的分离(Separation of Interface and Implementation) 单一引用点(Single Point of Reference)
  • 196. 信息隐藏信息隐藏(Information hiding)是指隐藏(删除)了程序构件操作的细节,只将访问该构件所必要的信息提供给访问该构件的其他构件。 在这一点上,OO方法和传统方法基本一致。因此,OO系统应支持信息隐藏,除提供隐藏等级说明的量度外,还应提供OO设计质量指标。
  • 197. 抽象抽象(Abstraction)使设计者只关心一个程序构件的主要细节(数据和过程两者),而不考虑底层的细节。 抽象指关注事物中与问题相关的部分(通常是一个角度或方面),而忽略其它不相关内容(细节)的一种思考方式; 抽象依赖于选择的问题领域和角度; 抽象是一种相对概念,在OO和传统开发方法中都被采用。如处于抽象的较高层次时,我们可忽略更多的细节,当处于抽象的较低层次时,可以引入更多的细节,即提供一个关于概念或项的更详细的看法。
  • 198. 抽象解决不了软件中过的规模问题 抽象能很好第降低单个软件原始的复杂度,但却解决不了软件中的规模问题。 ·根据7+/—2原荆,人能够同时处理的元素是5-9个,当软件中对象(种类)的数目超过一定限度后,人们就无法同时去思考和处理这些对象了。 模块化通过划分,帮助人们将同时要考虑的对象数目减少到普通智力的人能够胜任自如的地步。
  • 199. 模块化(Modularity) 指逻辑和物理上将事物分解为更小、更简单的分组(例如需求和类),从而满足软件工程化管理的需要; 模块化的实质--“分而治之”; 在面向对象的范式中,子系统、包、构件和对象本身都是模块化的实体元素。
  • 200. (本页无文本内容)
  • 201. 模块化带来模块间关系处理的问题 将系统划分为若干模块后,人们可以分别在各模块I内部处理那些数量已大幅减少的元素;但为了确保系统的完整一致,同时还必须在系统层面未处理这些模块及其之间的关系。 根据7+/—2原则,人能够同时处理的元素是5~9个,但此间实际上还有一个隐舍的条件,就是每个元素本身的复杂度也不超过一定限度,由此,如果模块对外展现的内客过多,仍然是无法在系统层面处理这些模块(及其关系)的。 封装在模块化划分的基础上迸一步简化了模块本身对外暴露的复杂度。
  • 202. 封装是指将特性(属性、行为)在物理上局限于一个单独的黑盒抽象中,且将它们的实现(和相关的设计决定)隐藏于公共接口背后; 在面向对象的范式中,从系统、子系统到对象都拥有封装的特性。 抽象类(还有接口)使得客户代码cl ient code只需要关注对象外在行为中与它相关的部分(忽略对象固有的但井不相关的其它外在行为.例如从其它类继承的操作); 对象的封装机制则封闭了对象所有的内部结构和内部行为,使客户代码无需关注对象内部的细节。
  • 203. 抽象 VS 封装 抽象和封装都是一种简化问题的思维方式,但它们实现简化的方式不同· 抽象剔除了不相关的内容,以突出重点;·相对应地,封装则隐藏了相关但不需要被知道 (不能被剔除)的内部细节,以减少依赖。 剔除与隐藏对于外部(客户)而言,其效果是等价的
  • 204. 简单模块化的问题 模块化通过划分来简化问题,但是简单的模I块化途径仍然处理不了大规模模的事物。 针对一个具有1000个元素的系统,我们当然可以将其划分为150个模块,这样每个模块的元素不超过7个,可以交给150个人来分担完成;但是同时为了确保系统的完整和一致,需要有人来协同150个人的工作,而这显然... 层次化为模块化划分提供了一种递归途径,以支持处理更大规模的事物。
  • 205. 层次化指任何组织为一种树状结构的抽象级别或顺序; 在面向对象的范式中,层次结构包括: 聚合层次结构Aggregation hierarchy 包容层次结构containment hierarchy 类层次结构class hierarchy 泛化层次结构generaDzation hierarchy 继承层次结构inheritance hierarchy 具体化层次结构specialization hierarchy 划分层次结构partition hierarchy 类型层次结构type hierarchy
  • 206. (本页无文本内容)
  • 207. 隔离关注面: 软件中包含的内容纷繁复杂、规模巨大;前述的抽象、模块化、封装、层次化等途径,实际上是从有形的实体结构方面帮助人们简化问题;然而软件还包含其它非实体(例如功能、性能、经济性等)方面Aspccts的内容,为了简化这些方面的问题解决,人们需要研究其它的方法和途径。 软件中的不同方面,往往被不同的涉众所关心,于是将其抽象为所谓的“关注面”。
  • 208. (本页无文本内容)
  • 209. 软件中胶着一体的各个方面(Aspect) 实体对象有明显的边界,可以方便地被分割。而方面却不是实体,其实质上只是附着在某个实体上的各类属性事物,例如,一个业务应用中的实体类,它需要被持久化(保存在数据库中),还要满足权限控制的要求,最后为了方便测试,还要支待日志记录等等;这些都是同一客体的不同侧面,它们不能独立于客体而存在。 另外,软件中的不同方面Aspects内客,常常相互渗透而交织一起,例如,上述实体类中的实现待久化代码与安全的就可能混合在一块;又如目标系统的进程组织就首期逻辑结构影响,
  • 210. 隔离关注面 Separation of Concerns 软件中的众多方面同时存在,并相互胶着,这使得开发活动变得更为困难和复杂。 前述“分而治之”已经给了我们启示——必须将复杂和大规模模的问题分解开未,分别加以解决。 然而方面不是实体,不能在物理上进行分割;不过,我们还是可以从逻辑上来将同一方面的内客组织在一起,并与其它方面的内客分隔开来。 无论是实体还是逻辑上的方面,都是涉众的“关注面”,由此我们引入一个统一的概念 “隔离关注面”。它使得人们能够分别地处理被分解的小问题,或同一问题的不同方面。
  • 211. 隔离关注面的途径: 在隔离关注面时,对于关系明确的场合,简单地分割就行了;但对于关系交织一起,需一同考虑的场合,要先做概略的总体考虑,然后再分开做处理。 隔离关注面的途径包括: 内外的隔离——部件对外公开的行为、及其与其它部件之间的关系,与其自身的内部细节,被分开来处理。 空间上的隔离——软件部件的分割(模块化)、同一部件不同侧面的分割(倒如某个类的逻辑包结构与其源码目录文件被分别考虑)等, 时间上的隔离——划分良好的部件,可以安排在不同的时间来完成;由于部件之间的依赖较弱,进而能够并行来进行(开发).时间上的隔离首先依赖于空间上的隔离,
  • 212. 质量因素的隔离——同时满足不同的质量要求是非常困难的。一则,同时为实现多个质量约束而设计将过于复杂而难以开展。二则,不同的质量属性之间可能是相互冲突的,要同时满足它们几乎是不可能的。因此,我们应当在不同的时机,分别去考虑实现不同的质量要求,即隔离质量关注点。 隔离关注面的其它途径还有:问题域与解决域(实现域)分离、人员职责与技能要求的划分等等
  • 213. 层次模式与架构中的分层 分层模式是一种将系统的行为或功能以层为 首要的组织单位来进行分配(划分)的结构模 式 通常在逻辑上进行垂直的层次Layer划分,在物理上则进行水平的层级Tier划分 分层要求层内的元素只依赖于当前层和之下的相邻 层中的其它元素(并非绝对的要求)
  • 214. 分层是为了隔离各层的关注面 分层的标准是: 将相似的事物分组在一起 将不同的事物分开 分层的目标在于隔离关注面: 不同层的元素与不同领域相关,例如,业务层关 注业务领域问题、中间件层关注适配操作系统的底层差异等软件自身问题。 各层的元素属于同一抽象级别,而不同抽象级别的特性不同,要求的处理方式也不一样。 稳定与变化的隔离。
  • 215. 指南:层次模式的优缺点 分层结构的优点: 上层可以直接使用下层,而不需要去了解下层的实现细 节 上层对下层透明,因而可以改变底层的实现,而不影响上层的应用 可以减少不同层之间的依赖 容易制定出层标准 层可以用来建立提供给上层的多项服务 分层结构的缺点: 层不可能封装所有的功能,一旦有功能变动,可能会波 及所有的层(例如领域对象增加一个属性) 穿过层次的调用,将引起效率降低;对于水平分布的层 级Tier划分,效率降低则极为明显
  • 216. 指南:划分应用层子系统 考察的因素 用户的分类 用户界面的粒度 用户界面交互逻辑及其关联的复杂度 用户界面的部署约束 划分方式 围绕系统的用户执行者Actor,来划分应用子系统(物理上隔离的可执行体) 使用工作视图模式,通过统一的主界面来访问应用子系统(逻辑上的)
  • 217. 构架分析时所面对的通用问题 开发健壮的构架(特别是分布式)通常要解决以下问题 选择候选层级Tier 如何保持会话状态 确定共通的用户界面交互机制 确定共通的数据存取机制(OR-Mapping ) 解决并发和同步冲突 支持事务处理 接口的定位与实例化机制(常用途径:名字服务) 设计统一的异常机制 安全机制的实施 上述问题中的大部分将抽象为分析机制
  • 218. 通用问题被总结为分析机制列表 GUI交互 遗留接口Legacy Interface 持久化Persistency 通讯Communication (IPC and RPC) 消息路由Message routing 分布式计算Distribution 事务管理Transaction management 进程控制与同步 (资源争用) 信息交换、格式转换 安全Security 安全Security 错误侦察/处理/报告 冗余备份Redundancy
  • 219. 面向对象的设计原则
  • 220. liskov替换原则(LSP)
  • 221. 子类型必须能够替换掉其基类型问题的根源是关于行为的: 基类中有的行为在子类中不存在或不适当。 IS A的本质是指行为的一致,而不是生活中的语言。 违反了LSP原则的本质是派生类的行为与基类中的不一致。 如果违反了LSP原则,常会导致在运行时的类型判断(RTTI)违反OCP原则。 例如:函数A的参数是基类型,调用时传递的对象是子类型,正常情况下,增加子类型都不会影响到函数A的,如果违反了LSP,则函数A必须小心的判断传进来的具体类型,否则就会出错,这就已经违反了OCP原则。
  • 222. 违反LSP导致违反OCP的简单例子
  • 223. 改善
  • 224. 例:会议管理系统问题描述 用来管理各种各样的会议参与者信息。数据库里面有个表Participants,里面的每条记录表示一个参会者。 因为经常会发生用户误删掉某个参会者的信息。所以用户删除时,并不会真的删除那参会者的信息,而只是将该记录的删除标记设为true。24小时以后,系统会自动将这条记录删除。但是在这24小时以内,如果用户改变主意了,系统还可以将这条记录还原,将删除标记设置为false。
  • 225. (本页无文本内容)
  • 226. 改善
  • 227. 例:GUI对象假定一个Component代表一个GUI对象,如按钮或者文本框等。
  • 228. (本页无文本内容)
  • 229. (本页无文本内容)
  • 230. 改善2
  • 231. 例:Line
  • 232. 改善:提取公共部分替代继承
  • 233. 例: PresistentSet
  • 234. 需求变更需求变更 想在该层次中加入持久性(PresistentSet),即可以把其中的元素写入流,稍后可能由另外的程序再从流中读回其中的集合。 可得的资源 能找到的,提供了持久化的第三方容器类不是一个模板类。它只接受基类为PersistentObject的派生对象。 自然的做法: PresistentSet把它的所有方法都委托给了第三方实例。例如Add方法将调用第三方实例的Add。
  • 235. (本页无文本内容)
  • 236. 问题表面合理,其实隐藏着别扭的设计: 加入到第三方持久性集合中的元素必须得从PersistentObject派生,由于PersistentSet只是把调用委托给第三方持久性集合,所任何要加入PersistentSet的元素也必须从PersislentOhjet派生,可是Set的接口没有这样的限制, 当客户程序向基类Set中加入元素时,客户程序不能确保该Set实际上是否是一个PenistentSet因而,客户程序没有办法知道它所加入的元素是否应该从PefiistentObject派生。
  • 237. 后果 如果客户企图向PersislentSet中添加不是从PersstentObject派生的对象,将会发生运行时错误。dynamic_cast会抛出异常,但是抽象基类Set的所有现在的客户都不会预计到调用这种异常。 由于Set的派生类会导致这些函数出现错误,所以对类层次所做的这种改动违反了LSP。 以前传递Set的派生对象时没有问题的函数,现在传递给它们PersistentSet对象时却会引发运行时错误。 调试这种问题很困难,因为这个运行时错误发生之处距离实际的逻辑错码很远。 逻辑错误可能是由于把PersistentSet传给了一个函数,也可能是由于向PersistentSet加入的对象不是派生自PersistentObject。
  • 238. 改善,通过约定解决问题: 不知道的人不要用:约定不让PersistentSet和PersistentObject暴露给整个应用程序。它们只被一个特定的模块使用。该模块负责从持久性存储设备读出所有容器,也负责把所有容器写入到持久性存储设备。 从容器写入流:Adapter:容器的内容先被复制到对应的PersistentObject派生对象中,再加入到PersistentSets,然后存入流中。 从流中写入容器:Adapter:先把信息从流读到PersistentSet中,再把PersistentObjects从PersistentSet中取出,复制到常规的对象中,然后再加入到常规的Set中。
  • 239. 问题: 需要不断向开发人员解释。 人员没有弄清楚或者不同意,就会违反这个约定。而一次违反就会致使整个结构的失败。 改善 PersistentSet和Set之间不存在IS—A关系,它不应派生自Set。 分离这个层次结构,但保留部分Set和PersistentSd之间有一些公有的特性。其实仅仅是Add方法致使LSP原则出了问题。 创建了一个层次结构,其中Set和PersistentSet是兄弟关系,统一在一个操作抽象接口下,就可对PersistentSet对象进行遍历以及测试成员等关系操作,但它不能够把不是派生自PersistentObject的对象加入到PersistentSet中。
  • 240. (本页无文本内容)
  • 241. DIP and IOC康凯
  • 242. 相关概念关于解耦 依赖倒置(DIP) 控制反转(IoC) 依赖注入(DI) 服务定位器(SL) 有些是手段,有些是原则,不过其间的差异并不太重要,更重要的是其共同点:其根本目标都是解除依赖,将组件的配置与使用分离开。 其它名词 服务 组件 框架 类库 应用程序
  • 243. 接口和实现分离 面向过程的接口与实现分离
  • 244. (本页无文本内容)
  • 245. (本页无文本内容)
  • 246. 依赖于特定函数的框架问题:双向依赖:
  • 247. 解决:通过回调函数消解框架到运用程序的依赖:
  • 248. 通过模板方式消解GUI框架到应用程序的依赖
  • 249. (本页无文本内容)
  • 250. 应用程序来创建“服务类” 、Reader和Writer类的实例对象。把Reader和Write注入“服务类”内部。 这样,“服务类”中的代码就只和抽象接口相关的了。具体实现代码发生变化时,“服务类”不会发生任何变化。添加新的实现时,也只需要改变应用程序的代码,就可以定义并使用新的Reader和Writer类。 “构造器注入” :应用程序通过Copy类的构造函数注入依赖关系。 “接口注入”:为Copy类抽象出一个注入接口,应用程序通过接口注入依赖关系。 “设值注入”:为Copy类提供一个设值函数,应用程序通过调用设值函数来注入依赖关系。
  • 251. (本页无文本内容)
  • 252. 电影清单的例子一个组件,用于提供一份电影清单,清单上列出的影片都是由一位特定的导演执导的。 class MovieLister { ... public Movie[] moviesDirectedBy(String arg) { List allMovies = finder.findAll(); for (Iterator it = allMovies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); if (!movie.getDirector().equals(arg)) it.remove(); } return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]); } }
  • 253. 其中真正想要考察的是如何将MovieLister对象与特定的finder对象连接起来。 要求:我们希望moviesDirectedBy方法完全不依赖于影片的实际存储方式。 这个方法只能引用一个finder对象, 而finder对象必须知道如何实现findAll的细节。 给finder定义一个接口: public interface MovieFinder { List findAll(); } 当要实际寻找影片时,就必须涉及到MovieFinder 的某个具体子类。在这里,我把“涉及具体子类”的代码放在MovieLister 类的构造函数中。
  • 254. 对抗变化从一个逗号分隔的文本文件中获得影片列表。 class MovieLister... private MovieFinder finder; public MovieLister() { finder = new ColonDelimitedMovieFinder("movies1.txt"); } 对抗变化: 文件清单的名字更改: 可以从一个配置文件获得文件名。 如果用SQL 数据库、XML 文件、web service,或者另一种格式的文本文件来存储影片清单: 用另一个具体的类来获取数据。该类从MovieFinder接口派生即可。 创建合适的MovieFinder派生类的实例: 不能对抗此变化。
  • 255. 客户代码public void testWithPico() { MutablePicoContainer pico = configureContainer(); MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }
  • 256. Spring 注入为了让MovieLister 类接受注入,需要为它定义一个设值方法,它接受类型为MovieFinder的参数: class MovieLister... private MovieFinder finder; public void setFinder(MovieFinder finder) { this.finder = finder; } 类似地在MovieFinder的实现类中,定义一个设值方法,接受类型为String 的参数: class ColonMovieFinder... public void setFilename(String filename) { this.filename = filename; }
  • 257. 配置文件设定配置文件:XML 文件是比较理想的配置方式。 movies1.txt
  • 258. 接口隔离原则(ISP)
  • 259. 例安全系统,有一些Door对象可以被加锁和解锁,并且Door对象知道自己是开着还是关着的。 需求变化: 实现一个TimedDoor,如果门开着的时间过长,它就会发出警报声。为了做到这一点,TimeDoor对象需要和一个Timer的对象交互。如何将TimerClient与TimedDoor类联系起来?
  • 260. 一种方案
  • 261. 问题Door依赖于TimerClient 并不是所有种类的Door都需要定时器。 违反LSP:不需时钟的派生类中需要提供TimeOut的退化方法。 不必要的复杂和重复性:使用这些派生类的客户程序被迫包含TimerClient的定义。 Door接口被TimeOut方法污染了: 持续的加入方法会使Door接口不断变胖。 每次Door中加入一个方法,其它派生类中都需要对自己不需要的方法提供退化处理。 客户对TimerClient接口的改变会影响接口使用者。 例如需要在Timer中注册多个超时通知(在TimeOut方法中加入超时ID),则即使不需要定时器的类也会受影响。
  • 262. 使用委托分离接口
  • 263. 使用多重继承分离接口
  • 264. 内接口与外接口
  • 265. 普通接口与智能接口
  • 266. 架构设计验证
  • 267. 软件可维护性软件的可维护性概述 软件可维护策略 软件可扩展性(Extensibility)设计策略 软件灵活性(Flexibility)设计策略 软件可插入性(Pluggability)设计策略 267
  • 268. 软件的可维护性概述软件可维护性的定义:软件能够被理解、校正、适应及增强功能的容易程度。 软件的可维护性、可使用性、可靠性是衡量软件质量的几个主要特性,也是用户十分关心的几个问题。 软件的可维护性是软件开发阶段的关键目标。影响软件可维护性的因素较多,设计、编码及测试中的疏忽和低劣的软件配置,缺少文档等都对软件的可维护性产生不良影响。软件可维护性可用下面七个质量特性来衡量,即可理解性、可测试性、可修改性、可靠性、可移植性、可使用性和效率。对于不同类型的维护,这七种特性的侧重点也是不相同。 268
  • 269. 可维护性和可复用性 一般来说,一个易于维护的系统,就是复用率较高的系统;一个复用率较好的的系统,就是一个易于维护的系统。但是,实际上,可维护性和可复用性是两个独立的目标。 软件维护就是软件的再生。一个好的软件设计,必须能够允许新的设计要求以比较容易和平稳的方式加入到已有的系统中去,从而使这个系统能够不断的的焕发出活力。 一个可维护性较好的系统,应当允许维护工作能够以容易、准确、安全和经济的形式进行。269
  • 270. 导致可维护性较低的原因: 1.过于僵硬:在系统中加入一个新的功能,不管大小都很难,不仅意味着建造一个独立的新的模块,而且因为这个新功能会波及很多其他模块,最后成跨越几个模块的改动。 2.过于脆弱:与软件的过于僵硬同时存在,是软件系统在修改已有代码时过于脆弱。对一个地方的修改,往往会导致看上去没有什么关系的另外一个地方发生故障。 3.复用率低:所谓复用,就是指一个软件的组成部分,可以在同一个项目的不同地方甚至另一个项目中重复使用。复用率低,指当一段代码,函数,模块的功能可以在新的模块或新的系统使用,但是已有代码依赖于其他很多东西,很难分开。270
  • 271. 4.黏度过高:一个改动可以保存原始设计意图和原始设计框架的方式进行,也可以以破坏原始意图和框架进行。第一种方法对系统的未来有利,第二种办法是权宜之计,可以解决短期的问题,但是会牺牲中长期的利益。如果一个系统中使用第二种方法比使用第一种方法容易,那么就是黏度过高。 设计的目标: 1. 可扩张性 2. 灵活性 3.  插入性 271
  • 272. 系统的可复用性: 复用性的重要性: 1. 较高的生产效率 2. 较高的软件质量 3. 恰当使用复用可以改善系统的可维护性 统的复用和面向对象的系统设计中复用的区别 1.传统的复用:代码的剪贴复用;算法的复用;数据结构的复用。 2.面向对象的设计的复用:在OO中数据的抽象化、继承、封装和多态是几个重要的语言特性,这些特性使得一个系统可以在更高的层次上提供可复用性。272
  • 273. 数据的抽象化和继承关系使得概念和定义可以复用; 多态性使得实现和应用可以复用; 抽象化和封装可以保持和促进系统的可维护性。273
  • 274. 可复用和可维护性的关系 1. 适当的使用复用,可以提高可维护性,即支持可维护性的复用,就是在保持甚至提高系统的可维护性的同时,实现系统的复用。 2. 适当提高系统的可复用性可以提高系统的可扩展性:系统的可扩展性由“开-闭”原则、里氏代换原则、依赖倒转原则和组合/聚合复用原则所保证。 3. 适当提高系统的可复用性,可以提高系统的灵活性。系统的灵活性由“开-闭”原则、迪米特法则、接口隔离原则所保证的。 4. 适当提高系统的可复用性,可以提高系统的可插入性。系统的可插入性由“开-闭”原则、里氏代换原则、组合/聚合复用原则和依赖倒转原则所保证。274
  • 275. 复用原则: 1. “开-闭”原则:OCP 2. 里氏代换原则:LSP 3. 依赖倒转原则:DIP 4. 接口隔离原则:ISP 5. 组合/聚合复用原则:CARP 6. 迪米特法则:LoD275
  • 276. 软件可维护策略从下面五个方面来阐述如何提高软件的可维护性: 1.建立明确的软件质量目标 如果要程序满足可维护性七个特性的全部要求,那么要付出很大的代价,甚至是不现实的,但有些可维护性是相互促进的,因此要明确软件所追求的质量目标。 2.使用先进的软件开发技术和工具 利用先进的软件开发技术能大大提高软件质量和减少软件费用。面向对象的软件开发方法就是一个非常实用而强有力的软件开发方法,用面向对象方法开发出来的软件系统,稳定性好,比较容易修改,比较容易理解,易于测试和调试,因此,可维护性好。276
  • 277. 3.建立明确的质量保证 质量保证是指为提高软件质量所做的各种检查工作。质量保证检查是非常有效的方法,不仅在软件开发的各阶段中得到了广泛应用,而且在软件维护中也是一个非常主要的工具。为了保证可维护性,以下四类检查是非常有用的: (1)在检查点进行检查。 (2)验收检查。 (3)周期性的维护检查。 (4)对软件包的检查。 4.选择可维护的语言 程序设计语言的选择对维护影响很大。低级语言很难掌握,很难理解,因而很难维护。一般来说,高级语言比低级语言更容易理解,第四代语言更容易理解,容易编程,程序容易修改,改进了可维护性。277
  • 278. 5.改进程序的文档 程序文档是对程序功能、程序各组成部分之间的关系、程序设计策略、程序实现过程的历史数据等的说明和补充。程序文档对提高程序的可阅读性有重要作用。为了维护程序,人们必须阅读和理解程序文档。 278
  • 279. 一些经验 1。首先在软件设计的时候就应尽量考虑全面,避免在后期开发到一定阶段之后在进行需求和功能上的改动。软件设计完成之后需要所有的项目负责人,项目经理,主要程序员,主要的测试人员一起讨论,讨论通过之后才能进行开发。 2。强调开发人员的代码规范。公司一定要形成自己的编程规范,所有的开发人员须遵守。 3。软件开发的时候必须遵守一定的流程规范。例如代码统一放在vss中进行管理,每次check in 之前必须完成code review,须有两个同事一起检查等,尽量在前期发现问题。 279
  • 280. 4。重视测试。除了单元测试与集成测试,由非开发人员和非设计人员来进行的黑盒测试非常重要。测试组未确认质量过关的软件不可release。 5。重视文档。设计文档,需求文档,程序架构文档,测试文档,用户手册,白皮书,等都应完善。 6 。在经历多个项目以后,要总结出一套对质量和可维护性的度量方法和标准,才能持续不断改进。 280
  • 281. 软件可扩展性设计策略应用框架 (Application Framework) 软件设计师用这个词描述有助于软件应用开发的一组可重用的设计和代码。 “结构”一词着实反映了任何框架的本质。在应用开发中,一个相当复杂的应用包含了如此之多的不断变化的细枝部分,而结构帮助我们将这些不断变化的细枝部分,组织成易于理解的少数几个部分。 “应用框架”为开发者提供了结构和模板,开发者以此为基线(Baseline)来构建他们的应用系统。
  • 282. 用户界面 1. 模型-视图控制器 (Model-View Controller, MVC) 2. MacApp 3. MFC业务领域 1. San Francisco 通用应用开发 1. CommonPoint 2. Java语言和运行时虚拟机的Java环境
  • 283. 应用框架作用模块化 (modularity) 可重用性 (reusability) 可扩展性 (extensibility) 简单性 (simplicity) 可维护性 (maintainability)
  • 284. 应用框架解析----框架开发过程在明确了需要应用框架之后,首先要确定框架开发过程包括的四个主要阶段:分析、设计、实现和稳定。分析设计构造稳定范围目标文档测试培训架构识别通用点和扩展点实现
  • 285. 应用框架解析----框架开发过程框架开发的通用技术: 通用点 (Common spots) 扩展点 (Hot spots):占位符号/扩展点 黑盒框架 (Black-box framework) 白盒框架 (White-box framework) 灰盒框架 (Gray-box framework) 设计模式 (Design pattern) 其中,通用点 、扩展点 、设计模式 是用于开发框架的技术,黑盒、白盒、灰盒是开发框架的方法。
  • 286. 286软件可复用性
  • 287. 软件的可复用性概述软件复用是将已有的软件及其有效成分用于构造新的软件或系统。它不仅是对软件程序的复用,还包括对软件生产过程中其它劳动成果的复用,如项目计划书、可行性报告、需求分析、概要设计、详细设计、编码(源程序)、测试用例、文档与使用手册等等。因此,软件复用包括软件产品复用和软件过程复用两部分的内容。 287
  • 288. 从对复用产品的了解程度和复用方式看,也可分为白盒复用与黑盒复用。黑盒复用指对已有产品或构件不需作任何修改,直接进行复用,这是理想的复用方式。它主要基于二进制代码的复用,包括可执行程序的复用和基于库(包括动态链接库和静态库)的复用。白盒复用指根据用户需求对已有产品进行适应性修改后才可使用。白盒复用一般为源代码一级的复用,以及相应的测试用例、文档等的复用。 无论白盒复用还是黑盒复用,都需要花费一定的代价熟悉和掌握被复用的软件系统。作为经济上的考虑,要求复用的代价必须大大小于重新开发的代价,否则就不应该考虑。 288
  • 289. 软件复用的一个关键因素是抽象。 软件的复用性很大程度上取决于对可复用对象的认识深度或者说可复用对象的抽象层次。抽象层次越高、与具体环境和特定细节越无关,则它被未来系统复用的可能性也越大。 领域分析则是进行抽象的有力工具。 软件复用基本原则 必须有可以复用的对象,所复用的对象必须是有用的; 复用者需要知道如何去使用被复用的对象。 289
  • 290. 在复用软件设计中,根据面向对象原理,可考虑几个方面: 1)封装性 2)重载 3)继承 4)聚合 5)多态性 中间件及相关软件是商业化的软件复用。仅看程序方面,软件复用后的制品也不只包括中间件软件,还包括软件框架、应用框架、通用业务构件等多种可复用形式。 290
  • 291. 常见问题: 在编程相关的各种书籍中一直在强调代码的可重用性,之前所写的代码也一直遵循着这一规则,但是最近越来越感觉这种规则很麻烦,当费劲心机的想使一段代码变得可重用,就不得不加上各种条件的判断,代码中充斥了各种if...else... 使代码变得很难看懂,有时自己都会被各种不同的属性与条件判断搞晕,这样的代码对于后期的维护来说肯定是十分困难的。所以我现在都是把不同的业务逻辑用不同的方法实现,即使有些逻辑是相似的,我也使用了不同的方法,虽然这样造成了许多重复代码,但是这样的代码看起来逻辑更清晰,也更利于后期的维护,包括转交给别人时也更容易让人看懂,尽管这样会造成代码量的增加。所以现在对代码的可重用性这个问题很困惑。291
  • 292. 建议: 在面向对象的语言里,如果if else 大多,也许说明抽象不够。多态 、功能边界定义不够、模块细化不够。 抽象错误的情况下考虑代码级别的重用是比较伤神的。业务描述真正正确以后,维护方案也真正就浮现出来了。反之业务错了可维护性设计也会预测错误,设计无用接口。 误区:面向对象使代码、逻辑更简单。292
  • 293. 持久层设计
  • 294. 数据库设计基础好的数据库设计在满足了软件需求之外,还要易维护、易扩充等等要求。此外,数据的一致性、冗余性、访问效率等问题也是设计时要关注的问题。 数据库设计要在充分理解需求的基础上进行。
  • 295. 一、一个好的数据库设计首先要满足用户的需求 二、一个好的数据库设计要便于维护和扩充 1、不要为各种编号字段的设定固定的意义 而是最好通过对照表来建立这种编号和意义的对照关系。例,给部门信息给出固定的编号;缺陷:由于这种对照关系既然不体现在数据库中,就必然要用业务逻辑来进行解释,这样一来,一有新的调整就不得不更新业务逻辑代码,容易不一致。
  • 296. 2、枚举信息要体现在相应在对照表中 而不是体现在使用该信息的表中的值字段,这样做的好处是当用户希望用该枚举信息作为查询条件的时候,通过参照表的方式可以很容易的建立这些信息,另外也避免了当多个表格中都含有该枚举信息有可能引起的不一致。
  • 297. 用关联表建立表和表之间的多对多关系 而不要用一个字段解析的方式进行,例,为了描述用户(UserInfo)和角色(RoleInfo)之间的关联关系,要建立对照表UserInfo_RoleInfo,不要在用户表中建立一个较长的字段,如Roles(用RoleID1; RoleID2...的形式构成)来代替,因为这样一来字段解释需要在业务代码相应的解析代码,二来由于Roles定长,无法满足用户角色的扩充。
  • 298. 三、一个好的数据库设计要具有“可读性” 如同编程书籍中反复强调的程序员一定要在代码的可读性方面下功夫一样,考虑到信息系统将来的升级和维护可能要要由另外一批人来进行,因此数据库设计必然也要具有可理解性。可参照提高代码可读性的常用方法,给出一些建议:
  • 299. 1、用设计文档来提高数据库设计的可读性 这点基本对应于“可读性”代码里面的注释。在一个合格的数据库设计文档中必须给出数据库中的每个表、每个字段、表间的关联关系以及各种约束的意义以及由来,从而有可能让开发者根据用户需求和设计文档就能理解正确数据库的设计。
  • 300. 2、给表和视图起一个有意义的名字 这点对应于coding规范中的变量和函数的命名,很显然,CustomerInfo的名字很容易联想到该表中含有客户信息,而把它命名为Table0001只能让人感到费解外。另外,如果DBMS提供表和视图名的大小写支持,该名称最好由每个构成单词(首字母大写)拼接而成。
  • 301. 3、用前缀给出表和视图内容之外的其他信息 如给参照表Ref_前缀,这样就可以让业务逻辑实现人员根据表的名字知道他所要操作的是不是张参照表,从而帮助他更快地理解详细设计,并有可能及早发现里面的错误。同样,给所有视图加上V_前缀,就可以让业务逻辑编程者很容易地知道他现在面临的是一个表还是视图,从而避免了对视图进行更新操作这种低级的错误。
  • 302. 4、给每一个字段起一个有意义的名字 如给CustomerInfo表中的电子邮件字段起名EMail让人很容易明白它的准确含义,而Field05则让人不知所云。基于同样的道理,数据库设计中也不能给字段起一个张冠李戴的名字。
  • 303. 5、字段命名要考虑上下文 举例来说,在UserInfo表中,用UserName来表示用户名字段就不如Name来的更加合适。这种情况画蛇添足的情况在对照表的设计中体现得尤为明显,如把部门对照表(Ref_Department)中的部门ID字段命名为DepartmentID,把部门名称字段命名为DepartmentName等等。
  • 304. 6、视图的设计不要牵扯到其他视图 与代码设计中函数调用最好不要嵌套过多层次相对应,为了便于数据库设计的阅读人能够很好地理解设计,视图最好直接建立在表上。
  • 305. 7、同一表中的记录最好不要相互引用 这种引用关系不仅让数据库设计的阅读人云里雾里,也不便于业务逻辑代码的编写。 8、关联表的命名用关联的表名中间加下划线连接构成 如学生(StudentInfo)和课程(CourseInfo)的关联表起名StudentInfo_CourseInfo。
  • 306. 四、一个好的数据库设计能够满足空间和效率的要求 对于一个信息系统来说,在实现用户需求的基础之上,保证一个较低的空间占用以及短的响应时间。
  • 307. 1、使用varchar而不要使用char字段 对于不定长信息如用户的简介信息,varchar的使用可以减少近一半的空间占用。当然这点不能一概而论,如用相应长度的char存储定长文本数据就比varchar来的合适。
  • 308. 2、不要使用BLOB字段存放“大数据” BLOB字段诚如其名,本身是为存储二进制大数据而出现的,同样的道理也适用于某些DBMS所引入的TEXT字段。因为对于一般信息系统而言,最长的字段往往是一些描述文本信息,而DBMS对char/varchar的长度基本能满足这种需求。建议设计者对一些很长的文本的最大允许长度进行确认,在此基础上参照DBMS中的开发手册来决定是否采用大字段。
  • 309. 3、不要使用设计器缺省的字段长度 这种做法一方面纵容了设计者对用户需求的一知半解以及对设计敷衍了事的不良习惯,另外一方面也在数据的存储上浪费了不少的空间,因为使用缺省字段长度的情况往往发生在字段上。
  • 310. 4、不要轻易使用unicode文本字段 DBMS对unicode的支持在帮助产品国际化的同时,也在一定程度上带来空间上的浪费,尤其是在当要存储的文本中的基本都是ASCII字符的情况下,这种浪费尤为明显。因此,建议设计者在选择unicode的理由,一定是出于国际化的考虑,而不是其他。因为大多数的大字符集和ASCII字符并存情况下所要碰到的问题基本上都已经由DBMS提供商解决。
  • 311. 5、使用预计算表来提高响应速度 跟数据仓库里面的某些思路相似,当业务逻辑中需要用倒根据历史信息得来的统计数据时,最好由独立于系统的预计算模块或相应的DW工具定期完成这些统计数据的预计算。
  • 312. 五、一个好的数据库设计可以简化业务逻辑的设计 所有的数据库设计都不是孤立的,它通过相应的业务逻辑实现(三层结构中还有表现层)来形成最终的产品,因此一个好的数据库设计应该能够帮助降低业务逻辑的编写难度,最起码不要给业务逻辑的设计、编码带来额外工作。
  • 313. 1、所有允许为空的字段必须是基于用户需求,而不是出于设计上方便的考虑。这样带来的好处是让详细设计中的某些错误和疏漏(如在设计中没有考虑对非空字段的内容检查)在编码和单元测试阶段就被发现,从而避免了进一步扩散,有助于提高软件的质量。
  • 314. 2、不要业务逻辑代码实现唯一性约束 对数据库表中的某些字段(或者多个字段的组合)的唯一性约束应该尽可能地加到数据库端。因为这种约束工作交给业务逻辑中去实现代价高昂而且不可靠。 3、关联约束一定要建立在数据库端 分析出设计中所涉及的主外键引用关系并体现在数据库设计中。这一条出于两点考虑:降低业务逻辑的编写难度和数据关联性约束的要求。
  • 315. 小提示定义标准的对象命名规范 表名:T_PRJNAME_XXX_R / B / H 字段名,用标准的前缀和后缀。 : 数字类型:_NO后缀; 字符类型: _CODE后缀。 表里有好多“money”字段:给每列增加不同的后缀,例如_AMT。 日期列:DATE_前缀或后缀。
  • 316. 索引 索引是从数据库中获取数据的最高效方式之一。95%的数据库性能问题都可以采用索引技术得到解决。 大多数数据库都索引自动创建的主键字段,此外外键也是常使用的键,比如运行查询显示主表和所有关联表的某条记录就用得上,要考虑建索引。还有,不要索引memo/note字段,不要索引大型字段(有很多字符),这样作会让索引占用太多的存储空间。
  • 317. 适用于聚集索引的情况包括: 1、含有大量非重复值的列。 2、使用BETWEEN,>,>=,<或<=返回一个范围值的列 3、被连续访问的列 4、返回大型结果集的查询 5、经常被使用连接或GROUP BY子句的查询访问的列 非聚集索引是完全独立与数据行的结构,表中的数据行不按非聚集键的次序存储,存储数据过程中不会影响数据表中记录的实际存储顺序。每个表可以创建249个非聚集索引 1 对于经常更新的列不宜建立聚集索引 2 具有高百分比的唯一值和不经常被修改的属性上创建聚集索引 例如:主键 3 若在一个表中既要创建聚集索引,又要创建非聚集索引,应先创建聚集索引,然后创建非聚集索引
  • 318. 不要为小型数据表设置任何键,假如它们经常有插入和删除操作就更别这样作了。对这些插入和删除操作的索引维护可能比扫描表空间消耗更多的时间。
  • 319. 不要把身份证号码选作键 不要使用身份证号码作为数据库的键。除了隐私原因以外,身份证号码需要手工输入。永远不要使用手工输入的键作为主键,因为一旦你输入错误,你唯一能做的就是删除整个记录然后从头开始。
  • 320. 实践评分系统 管理系统 会议系统
  • 321. 评分系统数据库与配置文件结合。 可以将所有的评分抽象出来,用统一的分值表示;具体分值对应的显示名用配置文件表示。 设置足够多的字段,代表不同的评审要素。 分值与配置文件的映射也写到配置文件中。
  • 322. 优点: 数据表设计大为简化。否则需要设计大量的字典;为每个类型的评审建立不同的数据表(每种评审类型的评审要素不同)。 将来用户增加一个评审要素(很有可能)时,不至于调整表结构; 将来用户增加一类评审类型(很有可能)时,只需增加一个模板文件,不至于增加一张表。 缺点: 字段冗余 针对某个分值做统计(业务限制)
  • 323. 专用表: 评委表 评审委员 文档表 项目表
  • 324. 设计之前,要估算可能的总数据量。 避免设计实际上无用的表。 表达父子关系的字典,可以用编码,也可以用父id。 1)ID+ParentID。 2)展开编码,编码合并。 2)展开编码,编码分离。 对结构复杂、数据量小、固定的数据,要倒入到内存中。 考虑到效率,要做反规范化的设计。用冗余的方式提高效率,计算的冗余及查询的冗余。
  • 325. 计算的冗余: 查询的冗余: 对查询冗余的改善:可以单独为查询结果做一张表。
  • 326. 表结构要照顾到界面显示,看界面显示是要做的查询来调整表。例如明细表单的弹出顺序。 、 甚至可以对一个查询报表设计一个对应的表
  • 327. 会议系统不要将一个特定领域的、小的对象,使用一个普遍的,大的对象,主要是不单一,且授权困难。 要考虑“虚”的对象。 要从对象业务的角度考虑,例如会议与会议室是一对一,还是一对多,如果是一对多,应该建立一张“关系表”,这必须通过业务对象分析得出。 同样,对系统中易变的部分可以使用配置文件。
  • 328. 数据表的导出与导入导出方式:导出时,要变ID,子表种记录主表的ID也要同步变。分3种,从一般到特殊分不同的层级。 1.按表结构逐张表定义。 对所有的表定义如下结构: aaa aaa
  • 329. 导入:分几种方法: 只针对特定的表做导入处理。 导出时,用xml设置表间关系,导入时读入关系进行处理。 导出时,用xml设置表间关系,并可表示多对多等关系,导入时读入关系进行处理。
  • 330. 将对象映射到数据库映射对象 将对象映射到数据库模式主要有下列3中方式: 将对象映射到它自己的表; 将对象映射到其它对象的表; 将对象映射到多个表;
  • 331. 将结构射到它自己的表 每个对象定义一个表 Struct PendingOrder{ int state; Date deliveryTime; } CREATE TABLE PENDING_ORDER{ PENDING_ORDER_ID NUMBER(10), STATE NUMBER(5) DELIVERY_TIME DATE }
  • 332. 将结构映射到宿主结构的表Emberded Value(嵌入值)模式【Fowler】,通常用来持久化简单值对象,且是宿主对象的子对象。该子对象的成员变量被映射到宿主对象的表字段。例如 strutct PendingOrder { Address deliveryAddress; } Struct Address{ String street1; String street2; String city; String state; String zip; }
  • 333. 可以将Address对象映射到ADDRESS表,但更简单的是映射到宿主对象中: CREATE TABLE PENDING_ORDER{ PENDING_ORDER_ID NUMBER(10), STATE NUMBER(5) DELIVERY_TIME DATE DELIVERY_STREET1 VAHRCHAR2(50), DELIVERY_STREET2 VAHRCHAR2(50), DELIVERY_CITY VAHRCHAR2(50), DELIVERY_STATE VAHRCHAR2(2), DELIVERY_ZIP VAHRCHAR2(10) }
  • 334. 该方案简化了数据库,减少了表数量。通过减少获取数据必需的连接(jion)数量,它还能提升性能。例如应用呈持续只需查询PENDING_ORDER表就能获取PendingOrder及其送货地址。
  • 335. 将对象映射到多个表当对象具有大量属性时,它还能提升性能。我们可以将使用不太频繁的属性映射到单独表,该表仅在必要时才被查询,这样就可不将整个对象映射到包含大量字段的单个表。
  • 336. 映射结构关系映射对象间关系的其他成员变量就稍微复杂一些。对象之间的关系有好几种,包括一对一、一对多、多对多。
  • 337. 映射一对一和多对一关系Struct PendingOrder Address deliveryAddress restaurant restaurant; } 一对一有两种映射方式: Embeded Value模式。 CREATE TABLE DELIVERY_ADDRESS{ PENDING_)ORDER_ID NUMBER(10)
  • 338. 多对一 通过被应用表的外键进行映射 CREATE TABLE PENDING_ORDER{ RESUAURANT_ID NUMBER(10) FOREIGN KEY(RESTAURANT_ID) REFERENCES RESTAURANT(RESTAURANT_ID) } PendingOrder – PendingOrderLineItem Restaurant – MenuItem
  • 339. 映射多对多关系顾客在下订单时可以使用多张优惠券 PendingOrder – Coupon 多对多可使用连接表(join table)进行映射 CREATE TABLE PENDING_ORDER_COUPON{ PENDING_ORDER_ID COUPON_ID }
  • 340. 映射继承
  • 341. 将继承层级关系映射到关系模式主要有3中方式: 每个继承体系一个表 每个类一个表 每个具体类一个表
  • 342. 每个继承体系一个表将继承体系里所有类都映射都一个表: CREATE TABLE COUPON{ CODE VARCHAR2(30), COUPON_TRPE VARCHAR2(100), MINIMUM NUMBER(10,2), PERCENT_DISCOUNT NUMBER(5,2), } 将Coupon层级体系都映射到Coupon表。
  • 343. 每个类一个表 每个具体类一个表 略
  • 344. 344UML设计建模
  • 345. 借鉴RUP中的设计流程
  • 346. 346UML用来描述模型的内容有3种,分别是事物( Things )、关系Relationships )和图( Diagrams )。
  • 347. 误区1—长蛇阵
  • 348. 误区2—活动图?序列图?
  • 349. 误区3—误用关系类型×?
  • 350. 误区4—状态图常见错误√×××常用前缀:已、未、待、正、刚
  • 351. 误区5—太挤√
  • 352. 误区6—粒度不统一
  • 353. 误区7—万金油图
  • 354. 如何改进高级阶段看懂图 能用一点用点多使用,不断积累中级阶段初级阶段表达出重点 和意图 按需建模掌握面向对象思想方法 做UML的主人
  • 355. 355UML中的关系UML中的关系(Relationships )主要包括4种: 1、关联关系 2、依赖( Dependency)关系 3、泛化( Generalization )关系 4、实现( Realization )关系
  • 356. 356一些常见问题辨析类的层次结构表示 属性与聚合 关联角色 关联类
  • 357. 357层次结构
  • 358. 358领域建模-重数
  • 359. 359细化类模型
  • 360. 360关联角色关联角色名出现在关联终端的旁边。当仅仅使用关联名不足够表达清楚后,可以用关联角色名来加强表达。 可以把每个名称都当成伪属性,关联角色名提供了一种可以遍历关联的方法。
  • 361. 361在创建类图时,应该为使用正确的角色名,而不是为每个引用引入一个独立的类。 因为角色名可以区分对象,所以附在一个类上的关联名必须唯一(可以把角色名想象成类的伪属性)。同样,角色名不应该与类的属性名重复。
  • 362. 362关联类正如可以用属性描述类的对象一样,也可以用属性来描述关联的链接,可以把这样的一组属性组成关联类。
  • 363. 363Actor的一些注意事项包括人与系统,总是外部的。 定义了边界。 Actor是“角色”,不是特定的人或特定的事。 要有恰当的名字。 可以泛化 不是可有可无的东西。另一方面它可以划分系统与外部实体的界限,是系统设计的起点。
  • 364. 364用例的一些注意事项是需求分析的第一步。用户首先关心功能。 用例是对一个系统或一个应用的一种单一的使用方式所作的描述,是关于单个活动者在与系统对话中所执行的处理行为的陈述序列。 不是事件流。 不是需求规格说明,但反映了主要的功能性需求。 识别用例最好是从分析流开始。 每个用例都是独立的。 用例名用动宾结构描述,不要写成一个名词。 用例是分层的,一般而言,高层/中层用例更有实际意义。 不要将不同的用例混合在一起。 用例与编码:低层的用例可以辅助编码,高层的不能。 扩展用例:基础用例不必知道扩展用例的细节,只提供扩展点。
  • 365. 365仓库信息系统的用例图
  • 366. 应用UML建模过程任务和活动: 有步骤、分层次地演进系统构架 将软件需求逐渐转变为软件的设计方案 保障软件的设计方案能够适应实施环境 总体框架: 全局分析-局部分析-全局设计-局部设计-细节设计
  • 367. 应用UML建模过程Use Case 实现设计模型层次构架软件需求 Use Case 报告词汇表Use Case补充规约
  • 368. 设计模型的内容与演进 广义的设计模型包括在分析和设计活动中得到的所有结果。设计模型由3部分组成: “Use Case实现”:反映软件需求对设计内容的驱动 层次构架(Layers):一种典型的构架模式,它将分析和设计的结果按照特殊到一般的等级分组,层次构架中的内容是后续开发活动的直接依据。 构架机制(Mechanism)描述可复用的设计经验应用UML建模过程
  • 369. 用例的拆分 用例分包 分包的目的: 1.为了让系统用例图能更清晰地表现出系统的业务逻辑关系和层次,让用户能够通过开发人员的描述看到他们自己的业务结构,并对此认可。 2.为了对系统进行模块的分割,这种分割将影响到系统今后的开发及系统的最终表现形式。
  • 370. 应用UML建模过程分包的基本原则: 1. 业务相关性:根据业务所属关系对用例进行分割划分,形成系统的业务包和子系统 2. 功能相似性:根据用例的功能关联关系,将功能实现相似的用例组合在一起形成系统的业务包和子系统。 分包的过程: 第一步先考虑业务相关性原则,进行第一层分包。 对于较大系统,可以考虑功能相似性原则进行第二层分包。 在完成了业务相关性分包之后,在进行同一业务的功能相似性划分可以提高系统某一模块的内聚性,同时,对于较大系统才可以划出比较有效的子系统,提高开发效率和成果。
  • 371. 全局分析 全局分析侧重于定义拟建系统所采用的构架以及影响构架的要素。全局分析充分利用相似或问题中的经验,避免在确定构架上浪费人力和物力。 选用架构模式 识别关键抽象:寻找那些无论在问题域或方案领域都具有普遍意义的概念点。 标识分析机制:将那些问题领域(应用逻辑)没有直接关联的计算机概念及相应的复杂行为表述为支撑分析工作的“占位符”。 选定分析局部:针对拟建系统的整体架构,找出那些蕴含相对高风险的局部作为工作内容。
  • 372. 全局分析识别“关键抽象”活动的主要依据是词汇表、Use Case报告和既往的经验。 关键抽象的含义:业务需求和软件需求中通常会揭示拟建系统必须处理的核心概念,这些概念同样将成为设计模型中的核心要素。关键抽象,就是那些能够始终贯穿分析和设计的类及相应对象。关键抽象往往对应重要的实体信息。
  • 373. 全局分析确定分析机制 确定分析机制的主要依据是“补充规约”、既往的经验以及关键抽象(概念模型);结果是一组被识别出来的“分析机制”。 什么是分析机制? 在分析过程中,“分析机制”向设计人员提供复杂行为的简明表述,降低(全局)分析活动的复杂性并提高(局部)分析活动的一致性。通过这些机制,可以使分析工作更有重点。借助分析机制,可以姑且不深究那些用于支撑核心功能(应用逻辑)但其自身并非核心功能的复杂行为,这些内容通常是解决特定软件技术问题的设计经验。 分析机制提供概念化的服务模式,“分析类”将使用这些服务模式的实例。分析机制的实例在系统架构中充当某些复杂行为的“占位符”。运用分析机制,可以避免分散全局分析的任务工作重点。一个比较典型 例子是数据存取问题,事实上这并不是一个简单的问题,但却是一个很普遍的问题,如果在分析和设计的初期就陷入数据存取功能的细节,势必严重影响设计人员对整体架构的把握。在全局分析任务中,可以利用一个称作“留存”(Persistency)的分析机制封装相关的技术细节。分析机制通常与应用逻辑中的特定内容关系松散,分析机制大多只涉及计算机软件技术的概念和要素。分析机制为那些与应用逻辑密切相关的要素提供必要的软件技术支撑,根据层次架构的基本原则,用于实现分析机制的涉及内容大多位于构架的中低层。
  • 374. 全局分析常见的分析机制 留存 分布式处理 安全性 进程间通信 消息路由 进程控制与同步 交易事务管理 信息交换 信息的冗余 错误检测、处理和报告 数据格式转换
  • 375. 全局分析安全留存分布处理信息交换报销单∨∨∨报销记录∨∨∨员工∨经理∨工资户头∨∨∨
  • 376. Use Case   优先级 风险 调整策略 结算当月报销费用 批复报账申请 审核报账申请 提交报销申请 提交借款申请 完成日常维护 制作报表 登录系统 1 数据库的响应速度     ★ ★ ★ ★       2 数据库的容量         ★ ★       3 与内部邮件系统的连接 ★ ★ ★ ★ ★ ★       4 与银行系统连接   ★   ★           5 与人事管理数据库连接         ★ ★     ★ 6 内部网有足够的带宽     ★ ★ ★ ★     ★ 7 数据备份             ★    
  • 377. 局部分析提取分析类 转述需求场景 整理分析类
  • 378. 局部分析提取分析类 针对用例模型中每一个Use Case定义系统的分析元素如何为Use Case 提供相应的行为。 分析类的定义和描述与Use Case 流建模是同时进行的。 确定分析类 分析类是概念层面的内容,与应用逻辑直接相关。分析类的实例所具备的行为,用于捕获拟建系统对象模型的雏形。 分析类直接针对软件的功能需求,因而分析类的实例的行为来自于对软件功能需求的描述(Use Case)。在使用分析类的时候,姑且不必关注某些与应用逻辑不直接相关的细节,特别是那些纯粹的软件技术问题。在某种意义上,可以认为分析类是技术解耦的。
  • 379. 局部分析分析类的类型划分 众多实践表明,如果立足于软件功能需求,拟建系统往往在三个维度易于发生变化:一、拟建系统和外部要素之间交互的边界;第二,拟建系统要记录和维护的信息;第三,拟建系统在运行中的控制逻辑。通常按照这三个变化因素的维度,将分析类划分为三种类型:边界类、控制类、实体类系统边界Use Case 行为协调基本信息
  • 380. 局部分析边界类的含义 用于描述拟建系统外部环境与内部运作之间的交互,主要负责内容的翻译和形式的转换,并表达相应的结果。 边界类主要用于描述三种类型的内容: 拟建系统和用户的界面,拟建系统和外部系统的接口以及拟建系统与设备的接口。不同类型边界的建模工作有所不同。如与外部系统接口主要关注通信协议;用户界面的建模主要关注交互内容,在概念上表达整个界面,而不是具体窗口体构件。 控制类含义: 通常控制类用于描述一个Use Case 所特有的事件流控制行为。控制类相当于协调人,被那些提出具体任务要求的类所共知:它自己通常不处理具体的任务,但它知道哪些类有能力完成具体的任务。 控制类将Use Case 所特有的行为进行封装,具有良好的隔离作用概念上,拟建系统的其他部分(边界类和实体类)将与use Case 具体执行逻辑形成松散耦合。
  • 381. 局部分析实体类的含义 实体类用于描述必须存储的信息,同时描述相关的行为。实体类代表拟建系统的核心信息,是拟建系统的重要部分,通常需要长期保存。 提取分析类 就是确定一组备选的、能够执行Use Case 中行为的分析类。 从文字说明的软件需求过渡到图形描述的设计内容是一个渐进的过程,提取一组备选的分析类是这个过程的第一步。 为了获得高内聚、低耦合的设计,使用三种不同的构造型识别和划分候选的分析类 边界类:通常一个Actor和Use Case之间的通信关联对应一个边界类 控制类:通常一个Use Case 对应一个控制类 实体类:在全局分析中的概念模型(关键抽象)集合
  • 382. 局部分析分析类在模型中的位置 在全局分析任务中,“关键抽象”位于一般应用层的关键抽象包;在局部分析任务中,分析类通常分布在特定应用层和一般应用层 -控制类通常放在特定应用层 -从特定的分析局部中发掘的实体类通常放在特定应用层 -表述用户界面的边界类放在特定应用层 -表述外部系统接口的边界类放在一般应用层
  • 383. 局部分析边界类的复用 边界类实例的适用范围和生命周期可能超越特定的Use Case的时间流内容。如果两个Use Case同时和一个外部因素(Actor)交互,它们往往可以共用同一边界类,因为它们表现的行为和承担的责任有可能存在很多复用的内容
  • 384. 局部分析控制类的变通:控制类实例的适用范围和生命周期通常和特定Use Case的事件流内容匹配。一个特定的 Use Case Realization对应一个控制类是典型的情况。 如果不同Use Case包含的任务之间有比较紧密的联系,某些控制类可以参与多个Use Case Realization。 换言之,不同的控制类可以参与同一个Use Case Realization。注意,这只是一种可能性,目的是重复利用相似部分从而降低整体的复杂性。通常,在明确多个Use Case Realization之后,才有可能发现它们之间的相通之处,在此基础上,这种做法才可能带来积极效果。不应该以此为目标,否则会适得其反。 Use Case Realization未必一定需要控制对象。在Use Case 事件流的逻辑结构非常简单的情况,边界类有可能在相关实体类的协助下实现相应Use Case的行为。简言之,如果Use Case Realization中的控制逻辑过于简单,那么(承担这类责任)控制类的必要性明显降低。
  • 385. 局部分析实体类的建议 实体类实例的适用范围和生命周期可能超越特定Use Case 的事件流内容。实体类通常不是某一特定Use Case Realization所专有。以下是针对提取实体类的一些建议: -充分利用关键抽象和已经被识别出来的分析类,可能是以往迭代中识别出的分析类,或者是当前迭代中从其它分析局部识别出的分析类 有时,和Use Case相关的Actor会在系统内部有一个实体类与之对应,尤其当该Actor的相关信息(属性、操作和关联)对实现拟建系统行为有直接贡献的时候。当然,Actor和与之相应的实体类在概念上截然不同,通俗讲,就是“内外有别”。 实体类不仅仅包含数据信息,还包含面向数据操作的行为,甚至是相当复杂的行为。
  • 386. 局部分析如果有一个拟建的实体类A仅仅被另一个类B引用,并且实体类A不具有明显的行为特征;那么可以考虑将实体类A作为类B的属性;相反,如果需求中的某一实体信息有可能被多个类引用,或者该实体信息具有显著的行为特征,通常将其建模为一个独立的实体类。
  • 387. 局部分析局部分析的目的是用面向对象的方法转述需求中的应用逻辑。提取分析类的活动中找出了用于转述需求的分析元素。用面向对象的方法转述Use Case中的内容,就是用分布在一组分析类中的责任分担Use Case所要求的行为。 描述分析类实例之间的消息转递过程过程就是将这些责任指派到分析类的过程。 这个过程是从软件需求过渡到设计内容的关键环节,其中核心概念是消息和责任的对应关系。Use Case的事件序列通常能用一组具有逻辑连续性的、介于分析类实例之间的消息传递加以表述。消息的发出者要求消息的接收者通过承担相应的责任作为对消息发出者的回应。一个分析类的实例在事件序列中接受的消息集合就是该分析类应承担责任的依据
  • 388. 局部分析
  • 389. 局部分析
  • 390. 局部分析
  • 391. 局部分析整理分析类 分析类是这样的类:它代表问题域中的简洁抽象; 分析类映射到真实世界的业务概念(并且据此仔细命名)。 问题域是首先产生软件系统(以及从此而来的软件开发活动)需求的域。通常,这是特定的业务领域,如在线销售或者客户关系管理。然而,务必注意,问题域可能根本不是任何特定业务活动,但 是可能产生需要软件在其上运转的一块物理硬件─这是嵌入式系统。基本上,所有业务软件开发服 务于某种业务需求,自动化一个已有业务过程或者开发具有有意义的软件组件的新产品。
  • 392. 分析类的最重要方面是,应该使用清晰和无歧义的方法映射到某个真实世界业务概念,如客户、 产品或账户。 这些工作的大部分是在需求工作流中捕获需求的活 动、创建用例模型和项目词汇表的活动中完成的。然而,进一步的澄清工作发生在创建分析类的用例 实现的过程中。 分析模型中的所有类都是分析类,而不是从设计考虑而产生的类(问题域),这一点是很重要的。 当你详细设计时,可能发现一个分析类被精化为一个或多个设计类。
  • 393. 局部分析分析类应该展示非常“高级层次”的属性集合,它们表示最终设计类可能具有的属性。我们可以 说分析类为设计类捕获候选属性准备了条件。 分析类操作,在高级层次上,说明类必须提供的关键服务。在设计中,它们将成为实际的、可实 现的操作。然而,一个分析级的操作常常分解成多于一种的设计级的操作。 分析类的最小形式由以下部分组成: 名称─这是强制的。 属性─尽管只有候选属性的重要子集在此时建模,但是属性的名称是强制的。属性类型被认 为是可选的。 操作─ 在分析中,操作仅是类职责的高级层次的陈述。只在它们对于理解该模型很重要时, 显示操作的参数和返回类型。
  • 394. 分析类的职责职责是类和它的客户之间的契约或者是类对它的客户的义务。本质上,职责是类提供给其他类的 服务。分析类具有直接同类(由它的名称所表达)的目的相一致的以及直接同该类正在建模的真实世 界“事物”相一致的非常内聚的职责集合,这一点是至关重要的。例如ShoppingBasket示例,你将期 望该类具有如下职责: 向购物篮添加商品; 从购物篮删除商品; 显示购物篮中的商品。 这是内聚的职责集合,一切都是为了维护客户选择的商品集合。它是内聚的,因为所有的职责都朝着相同的目标─维护客户已经选择的商品集合。实际上,我们能够把这些职责概括为非常高级层 次的职责“维护购物篮”。 同样,向 ShoppingBasket 中添加如下职责:
  • 395. 分析类的职责验证信用卡; 接收付款; 打印收据。 但这些职责似乎同购物篮的目的或直觉语法不匹配。它们不是内聚的,显然应该赋予其他什么类─ 可能是类CreditCardCompany、类Checkout以及类ReceiptPrinter。把职责适当地分配给分析类 以最大化每个类中的内聚性,是很重要的。 最后,良好的类与其他类之间具有最低数目的耦合。我们以给定类与其他类具有关系的数目来度量类间的耦合度。类间职责的平均分布趋向于产生低耦合度。把控制或职责局限于单一的类趋向于增 加到该类的耦合度。
  • 396. 分析类经验法则以下是创建形式良好的分析类的一些经验法则。 每个类大约3~5个职责─典型地,类应该保持尽可能简单,这通常限制类能够支持的3~5个职 责的数目。先前ShoppingBasket 的示例是带有小的和可管理数目职责的专注的类的好的示例。 不存在独立的类─好的OO分析和设计的精华是,类相互协作让用户受益。同样,每个类应该 同小部分类协作以提供所期望的功能。类可以把它们的一些职责托付给专注于特定功能的其他“辅助”类。 当心很多非常小的类─ 有时很难取得正确的平衡。如果模型看起来具有大量的非常小的类, 每个类都具有一个或者两个职责,那么你应该仔细查看以把一些小的类整合成更大的类。
  • 397. 当心少数几个非常庞大的类─上述的反面是,模型具有很少的类,但每个类都是具有职责数 量(>5)的庞大的类。解决问题的策略是依次查看这些类,看看是否每个类都能够被分解成为 两个或者多个能够承担恰当数目职责的、更小的类。 当心“伪类”─伪类其实是一般的过程函数,它伪装成类。 当心万能类─ 存在似乎能够承担任何工作的类。看看名称为“system”和“controller”的 类!处理这个问题的策略是看看万能类的职责是否能够分解成内聚的子集。如果是,每个这些 内聚职责集合可能独立成类。这些更小的类协作以实现由原始万能类所提供的行为。
  • 398. 分析类经验法则避免深度继承树─设计良好的继承层次的本质是继承层次中每个抽象层次应该具有良好定义 的目的。容易添加很多实际上不能服务于任何目的层次。 实际上,通常的错误是使用继承来实 现一种功能分解,其中每个抽象层次恰有一个职责! 无论从哪个方面来讲,这都是无意义的,是会导致复杂的、难以理解的模型。 在分析中,类代表业务事物,而业务事物趋向于形成更宽(不超过三层)的继承层次。 我们把三层或者更多层次的继承认为是“深度”继承。
  • 399. 局部分析实体类与属性的差异 类的属性和独立的实体类有所不同。但是客观地讲,它们之间的界限并不非常清晰。在以下两种情况,通常用独立的实体类为特定客体(信息)建模。 -客体具有比较复杂的自身行为 -客体具有独立标识(Identification),有可能被多类对象共享或者传递。 相反,在以下情形,通常用类的属性为特定客体建模。 -客体除了非常简单的取/赋值操作(get,set等),不具备更多其他行为。 -客体不需要独立标识,仅供一类对象使用。
  • 400. 全局设计 全局设计任务将在拟建系统的全局范围内,以分析活动的结果为出发点,将现有的分析类映射成模型中的设计元素,明确适用于拟建系统的设计机制,调整内容逐渐充实拟建系统构架。 全局设计任务中,有不同侧重的三项活动:
  • 401. 全局设计确定核心设计元素。 在系统架构的中高层,以分析类为出发点,确定相应的核心设计元素,具体讲就是设计类和子系统接口。 引入外围元素 在系统构架的中低层,以分析机制为出发点,确定能够满足相关分析类要求的设计机制;根据设计机制所依赖的技术实施手段,将那些能够提供基本服务的构件所对应的逻辑内容引入设计模型。 优化组织结构 按照高内聚、低耦合的基本原则,整理逐渐充实起来的层次构架内容。 全局设计任务的总体思路是从上向下充实层次构架中的内容,然后作贯穿层次的调整。
  • 402. 设计模型和数据模型的关联数据模型 数据模型描述拟建系统中留存数据的逻辑和物理内容与结构。 数据库设计师负责建立和维护数据模型。最初的数据模型在确定系统整体构架时建立。主要内容是那些对体系构架影响较大的留存数据,数据模型在后续活动中不断地演进和完善。
  • 403. 设计模型和数据模型的关联设计模型和数据模型的映射 -映射实体 基于具体内容的视角,设计模型中留存类的一个实例(对象)和数据模型中表的一条纪录(行)相对应。基于组织结构的视角,留存类和表相对应,类的名称对应表的名称,类的属性名称于类型对应表的列名与类型。通常,没有必要将留存类的所有属性均映射到数据模型当中,只需映射那些需要留存的属性。
  • 404. 设计模型和数据模型的关联 -映射关系 关联关系的映射 泛化关系的映射
  • 405. 整理设计文档分析和设计活动中的主要文档 -设计指南。描述设计过程与设计向实施过渡中所遵循的原则和方法 -Use Case 实现报告。展示分析和设计内容与需求中应用逻辑的关联 -设计模型纵览报告。展示拟建系统构架的整体逻辑 -设计包报告。展示特定设计包中的元素和图述 -设计类报告。展示特定设计元素及其相关内容。
  • 406. 领域模型及业务逻辑层在架构设计中的实现
  • 407. 面向对象分析需求分析:确定系统的目标和特性。 选择领域对象。 确定对象的职责。 确定动态场景。
  • 408. 确定系统的目标和特性例,pos收银系统,客户的需求列表: 扫描商品并自动为它们定价。 知道是否正在销售某种商品。 自动计算销售总量、计算税额。 处理购买和退货。 处理各种付款方式:现金、支票或记账。 授权支票和信用卡。 接到现金或支票时,计算找零。 记录关于客户交易的所有信息。 平衡收银机抽屉中的现金与POS系统所记录的数额。
  • 409. 确定系统的特性一般来说,可以从以下4个方面来考虑: 记录重要的信息:例如需要写入日志、数据库、磁盘文件的信息。 系统的流程:例如管理过程,交易过程。 结果分析:例如分析经营成果。 与其它系统的交互:例如第三方提供的系统。
  • 410. 确定记录重要信息(日志)的特性向领域专家询问有关系统必须每日记录的信息。 记录每件产品对应的当前价格 (与通用产品代码UPC对应) 。 记录当前税金的信息(类别、税率、有效日期)。 记录各收银员的权限信息。 记录应继续供应店中的哪些商品。 记录每笔销售的结果。
  • 411. 确定交易过程的特性考虑得到所有这些所需的信息后,系统可以为我们自动做那些事情。 从交易进行所必须的步骤入手: 为商品标价:根据UPC给每种商品标价。 计算总销售额:进行小计,计算销售税额并进合计。 收款:收现金、卡、支票、或记账方式。
  • 412. 分析交易成果交易发生后,分析交易的成果是促进经营的关键,是评估商店运作情况、决定应该采取什么行动的唯一方法。 计算每种商品已售出多少。 计算以现金、支票或信用卡形式成交的销售中,已收入的钱数。 评估每位收银员的表现。 评估每个店的运用情况。
  • 413. 确定与其它系统交互的特性新的POS系统应该能与尽可能多的支票系统、信用卡认证系统进行交互,获得授权。 目标确定后,利用系统的目标和特性指导我们如何理解正在研究的系统的职责。
  • 414. 建立问题域对象模型对象模型中需要要哪些对象?从哪儿入手? 运用对象模型指导和组织工作。 从业务领域的角度:与当前业务相关的类。 从人机交互的角度:UI、窗口和报表。 从数据管理的角度:数据库。 从与系统交互的角度:其他系统。 暂不考虑:在本系统范围之外。 目的是为了将系统划分为有意义的、松耦合的子集。
  • 415. (本页无文本内容)
  • 416. 问题域组件:由正在建模的问题直接对应的对象组成。这个组件中的对象与技术无关,它们对其余组件中的对象知道得很少或根本不知道。 与人交互组件:由提供问题域对象和人之间接口的对象组成,在对象模型中,这些对象大多对应于特定的窗口和报表。 数据管理组件:由提供问题域对象与数据库或文件管理系统之间接口的对象组成。在对象模型中,这些对象大多对应于那些需要要提供永久性保存支持,和提供搜索支持的特定问题域对象。
  • 417. 与系统交互组件:由提供问题域对象,和其他系统之间接口的对象组成。系统交互象封装了通信协议,使伴随它的问题域对象摆脱了这种低层的、具体的实现内容。 此外还有一种模型组件——“暂不考虑”组件:这是一个垃圾桶,其中保存那些已经考虑到,但由于技术或非技术的原因,决定眼下暂不予考虑的组件,把这个组件和其他儿个组件放在一起使用,就能说明系统中包括什么、不包括什么。
  • 418. 使用组件模型的意义高内聚、低耦合、可重用: 利用这种方式对类进行组织,可以使每个模型组件内部的建模更简单。 增加了下次进行重用的可能性:下次,即便可能选择使用不同的与入交互类,但你仍然可能希望重用这里的某些问题域类。 如果把这些模型组件打碎,整个模型就很难理解,而且将来除非你选择完全相同的问题域、与人的交互、数据管理及与系统交互的方法,否则,重用是根本不可能的。 因此,应用模型组件,把模型组件作为总体纲要、选择对象的指南,利用它们使类保持良好的组织结构,以便于理解和重用。
  • 419. 组件的设计方法在每种组件中,都可以使用如下的策略和模式: 选择对象。 确定职责。 描绘动态情景。
  • 420. 问题域对象在问题域中,寻找系统需要了解或处理的对象。那么如何考虑需要选择那些对象? 一般来说,我们可以从这个方面入手来选择对象:考察执行者、参与者、地点、事件。 本系中,执行者是:个人 个人:(抽象的对象,例如代表收银员的基本属性,可以为其它对象复用)。
  • 421. “选择参与者”策略分析可个参与者是如何以参与到待建系统中的。 每个参与者在整个过程中以一种或多种方式参与,带着“不相同的帽子”的,应建模为不同的参与者。 参与者的例子: 代理人、申请人、买主、出纳员、顾客、经销商 、代理人、雇员、投资人、制造商、会员、公务员、销售员、货主、学生、订户、管理员、教师、批发商、工人。。。
  • 422. 422设计模式本质
  • 423. 423设计模式在实际开发中的运用复用现有的、高质量的、针对常见的重复出现问题的解决方案。 建立通用的术语以改善团队内部的沟通。 将思考转移到更高的视角。 判断是否拥有正确的设计,而不仅仅使一个可以运行的设计。 改善代码的可修改性。 发现“庞大的继承体系”的替代方案。
  • 424. 424GoF中的模式分类
  • 425. 425设计模式的特点设计模式最根本的意图是适应需求变化 隔离变化的部分与不变的部分,将之封装起来。 针对接口编程,而不要针对实现编程 达成高内聚合低耦合,提高复用 提倡优先使用聚合,而不是继承
  • 426. 426例一个日志记录工具。目前需要提供一个日志API,提供客户方便地调用。 该日志要求被记录到指定的文本文件中,记录的内容属于字符串类型,其值由客户提供。 可以容易地定义一个日志对象。 public class Log { public void Write(string target, string log) { //实现内容; } } 当客户需要调用日志的功能时,可以创建日志对象,完成日志的记录: Log log = new Log(); log.Write(“error.log”, “log”);
  • 427. 427
  • 428. 428
  • 429. 429例我们需要设计一个数据库组件,它能够访问Sql Server数据库。如果用ADO.Net,需要使用如下的对象: SqlConnection, SqlCommand, SqlDataAdapter等。 不用模式的做法:可以直接创建这些对象: SqlConnection connection = new SqlConnection(strConnection); SqlCommand command = new SqlCommand(connection); SqlDataAdapter adapter = new SqlDataAdapter();
  • 430. 430Connection对象:
  • 431. 431
  • 432. Abstractfactory(抽象工厂)
  • 433. 意图 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 别名 Kit 动机 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作,同时由于需求的变化,往往存在着更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?这就是我们要说的抽象工厂模式。其意图是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  • 434. 结构图
  • 435. 效果AbstractFactory模式有下面的一些优点和缺点: 优点 分离了具体的类: 抽象工厂模式帮助你控制一个应用创建的对象的类,因为一个工厂封装创建产品对象的责任和过程。它将客户和类的实现分离,客户通过他们的抽象接口操纵实例,产品的类名也在具体工厂的实现中被分离,它们不出现在客户代码中。 使产品系列容易交换: 只要更换相应的具体工厂即可(经常用工厂方法来实现)。而一个抽象工厂创建了一个完整的产品系列,所以具体工厂变换后,整个产品系列会立刻改变。 有利于产品的一致性: 当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要。而AbstractFactory很容易实现这一点。
  • 436. 效果缺点 难以支持新产品的类型: 如果抽象工厂的基类要发生变化,那么针对每个产品系列的具体工厂也都要发生变化,显然这是很麻烦的。 不能创建有着不同个数对象的系列。 注: 抽象工厂模式主要在于应对“新系列”的需求变化。其缺点在于难于应付“新对象”的需求变动。如果在开发中出现了新对象,该如何去解决呢?这个问题并没有一个好的答案。 “GOF《设计模式》中提出过一种解决方法,即给创建对象的操作增加参数,但这种做法并不能令人满意。事实上,对于新系列加新对象,就我所知,目前还没有完美的做法,只有一些演化的思路,这种变化实在是太剧烈了,因为系统对于新的对象是完全陌生的。” -李建忠
  • 437. 437策略(Strategy)模式
  • 438. 438练习下面是一堆杂乱的类与接口: 动作冒险游戏。包括代表游戏角色的类,以及武器行为的类。每个角色一次只能使用一个武器,但是可以在游戏的过程中换武器。 任务: 1.安排类。 2.找出一个抽象类、一个接口、以及八个类。 3.在类之间画箭头。 A.继承。B.实现接口。C.「有一个」关系。 4. 把setWeapon() 方法放到正确的类中。
  • 439. 439原始的类与接口
  • 440. 440例:电子零售系统该电子零售系统必须处理来自不同国家的销售定单。例如在美国与加拿大。需求如下: 请况过程美国据UPS的价格计算运费 使用美国邮政规则检查地址 根据销售额或服务,按照当地税收规则计算税费金额 使用美元处理款项加拿大根据主要的加拿大托运公司的价格计算运费 使用加拿大邮政规则检查地址 根据销售额或服务,按照加拿大各省的税收规则计算税费金额(GST和PST) 使用加拿大元处理款项
  • 441. 441分析矩阵
  • 442. 442桥接(Bridge)模式
  • 443. 443意图 “将抽象部分与它的实现部分分离,使它们都可以独立地变化”。 抽象部分是指“不同的事物在概念层次上的联系”。分离是指“让各部分的行为各自独立,或至少显式指出关联”。
  • 444. 444例通过引入一个Rectangle 抽象类,利用了这一事 实:不同的Rectangle派 生类之间唯一的差异是如 何实现drawLine方法。 V1Rectangle类的实现方 式是:保存一个DP1对象 的引用,使用DP1对象的 draw_a_line方法。 V2Rectangle类的实现方 法是:保存一个DP2对象 的引用,使用这个对象的 drawline方法。
  • 445. 445需求变化用户要求支持另一种形状——圆形。
  • 446. 446识别变化首先识别出“什么在发生变化”。在上述的例子中,变化点是“不同类型的形状”和“不同类型的画图程序”。共同的“概念”则是“形状”和“画图程序”。 用Shape类来封装拥有的形状的概念。形状有责任知道如何画自己。Drawing对象负责画线和圆。
  • 447. 447描述变化下一步是描述出现的特定变化:Shape类拥有矩形和圆形。画图程序分别拥有一个基于DP1的对象(V1Drawing)和一个基于DP2的对象(V2Drawing)。
  • 448. 448桥接模式
  • 449. 449观察者(observer)模式康凯
  • 450. 450
  • 451. 451命令(command)模式康凯
  • 452. 452意图 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。 别名 动作(Action),事务(Transaction) 动机 有时必须向某对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息。 例如,用户界面工具箱包括按钮和菜单这样的对象,它们执行请求响应用户输入。但工具箱不能显式的在按钮或菜单中实现该请求,因为只有使用工具箱的应用知道该由哪个对象做哪个操作。而工具箱的设计者无法知道请求的接受者或执行的操作。命令模式通过将请求本身变成一个对象来使工具箱对象可向未指定的应用对象提出请求。这个对象可被存储并像其他的对象一样被传递。这一模式的关键是一个抽象的Command类。
  • 453. 453例子
  • 454. 454结构
  • 455. 455其它设计模式
  • 456. 456VISITOR模式 该系列中的模式如下: VISlTOR模式 ACYCLIC VISITOR模式 DECORATOR模式 EXTENSION OBJECT模式
  • 457. 457例是一个常见的问题:例如,有一个Modem对象的层次结构。基类中有所有调制解调器的公共方法。派生类代表不同调制解调器类型的驱动程序。
  • 458. 458问题假设需要向该层次结构中增加一个新方法confrgureForUnix。使之可在UNIX下工作。在每个调制解调器派生类中该函数的实现都不相同。(假设每个不同的调制解调器在UNIX中都有自己独特的配置方法和行为特征)。 问题:直接增加configureForUnix方法其实回避了一个问题:对于Windows如何处理? MacOS? Linux? 必须针对所使用的每一种新操作系统都要向Modem层次结构中增加一个新方法? 这种做法是丑陋的:我们永远无法封闭Modem接口。每当出现一种新操作系统时, 就必须更改该接口并重新部署所有的调制解调器软件。
  • 459. 459VISITOR模式的结构
  • 460. 460VISITOR+组合模式VISTTOR模式的一个非常常见的应用是,遍历大量的数据结构并产生报表。这使得数据结构对象中不含有任何产生报表的代码。如果想增加新报表,只需增加新的访问者,而不需要更改数据结构中的代码。这意味着报表可以被放置在不同的组件中,并且仅被那些需要它们的客户单独使用。
  • 461. 461例:报表生成器一个表示材料单的简单数据结构。从该数据结构可以生成无数的报表。 两个可以统计的量:成本;数量 例如可以生成一张一个组合件总成本的报表,或者生成一张列出了一个组合件中所有零件的报表。
  • 462. 462VlSITOR模式的解决方法
  • 463. 463其它模式问题: 考虑前面的Modem层次结构。假设有一个具有很多使用者的应用程序。每个使用者都可以坐在他的计算机前,要求系统使用该计算机的调制解调器呼叫另一台计算机。有些用户希望听到拨号声,有些用户则希望他们的调制解调器保持安静。
  • 464. 464DECORATOR模式DECORATOR模式通过创建一个名为LoudDialModem的全新类来解决这个问题。 LoudDialModem派生自Modem ,并且委托给一个它包含的Modem实例。它捕获对dial函数的调用并在委托前把音量设高。
  • 465. 465多个Decorator有时,在同一个类层次结构中可能存在两个或者更多的装饰器(decorator)。 例如, 可能希望用LogoutExitModem来装饰Modem层次结构, 当Hangup被调用时,它会发送字符串 "exit"。这个装饰器必须要重复已经在LoudDialModem中编写过的所有委托代码。
  • 466. 466SOA的设计思想
  • 467. 467SOA的架构的特点服务(Service) 定义良好的,自包含的,不依赖于上下文和其它服务的一组功能 SOA(Service-Oriented Architecture) 本质上是一组服务的集合 服务之间相互沟通 可以是简单的数据传输,或者是由两个或多个服务共同参与的一些活动,SOA也包括Service之间的连通技术。
  • 468. 468OO vs. SOA –OO的扩展遇到了挑战 随着时间的推移,接口继承的复杂度在累积 随着系统间距离的延伸,调用成本在上升,类型系统的不同步 扩展组件的功能成本高,不可确定未来需求,不可堆叠的扩展方式 重用与标准化,重用是OO的第一原则,难以维持和维护复杂的重用标准和机制
  • 469. 469–OO vs. SOA OO仍然适用于服务的开发 明显的性能优势 成熟的设计与开发方法 SOA适用于系统的互联 互操作性的要求强于性能的要求
  • 470. 470SOA 既不是一种语言,也不是一种具体的技术,它是一种新的软件系统架构模型。 SOA 最主要的应用场合在于解决在Internet环境下的不同商业应用之间的业务集成问题。 SOA 架构的出现为企业系统架构提供了更加灵活的构建方式,如果企业架构设计师基于 SOA 来构建系统架构,就可以从底层架构的级别来保证整个系统的松耦合性以及灵活性,这都为未来企业业务逻辑的扩展打好了基础。
  • 471. 471特性: 松耦合性 松耦合性要求 SOA 架构中的不同服务之间应该保持一种松耦合的关系,也就是应该保持一种相对独立无依赖的关系; 位置透明性 位置透明性要求 SOA 系统中的所有服务对于他们的调用者来说都是位置透明的,也就是说每个服务的调用者只需要知道他们调用的是哪一个服务,但并不需要知道所调用服务的物理位置在哪里; 协议无关性。 协议无关性要求每一个服务都可以通过不同的协议来调用。
  • 472. 472
  • 473. 473BPM、EA 和OOAD
  • 474. 474OOADOO 支持应用程序分析、设计和开发的完整生命周期 SOAD 应该尽可能多地利用OO 分析技术 将OO 分析成功地应用于SOA 项目 必须一次分析多个系统,用例模型必须继续扮演重要的角色 SOAD 主要是流程,而不是用户驱动的。SOAD 需要BPM 和用例建模活动之间的强链接 OO设计的目标是能够进行快速而有效的设计、开发以及执行灵活且可扩展的应用程序 从OO 的角度看,每件事情都是对象。
  • 475. 475与SO 有关的OO 设计的主要问题 OOAD 粒度级别集中在类级 对于业务服务建模来说,这样的抽象级别过低 诸如继承这样的强关联产生了相关方之间一定程度的紧耦合(因而具有依赖性) SOA 试图通过松耦合来促进灵活性和敏捷性 在SOA 中还没有服务实例的跨平台继承支持和表示法来避免需要处理服务生命周期维护管理问题(如远程垃圾收集)
  • 476. 476SOAD 服务定义层次 SOAD服务 可能包括许多协作或编排服务 并没有排除RUP 采用的OO 观点,而是在其上实现了另一个抽象层。 其作用是封装作为正式的跨层接口结构中的软件服务的组件
  • 477. 477SOAD的基本要求: 必须正式(至少半正式)地定义流程和表示法 通过选择和组合OOAD、BPM 和EA 必须有结构化的方法来概念化服务: OOAD提供了应用程序层上的类和对象,而BPM 具有事件驱动的流程模型 SOAD 需要将它们结合在一起 方法不再是面向用例的,而是由业务事件和流程驱动的 – 用例建模是在更低的层次上作为第二步进行的 方法包括语法、语义和策略 需要特别的组合、语义代理和运行时发现
  • 478. 478质量因素的考虑 构思良好的服务给业务带来了灵活性和敏捷性 通过松散耦合、封装和信息隐藏使重构更加容易 设计良好的服务是有意义的,并且不只适用于企业应用程序 服务之间的依赖性减到最少,并且是显式声明的 服务抽象是内聚、完整和一致的 例如,在设计服务及其操作签名时应该考虑其CRUD 通常假定服务是无状态的 领域专家无需深奥的专业知识就可以理解服务命名 在SOA 中,所有的服务都遵循相同的设计体系(通过模式和模板连接的)和交互模式;底层架构模式容易识别 服务和服务使用者的开发除了领域知识之外只需要基本的编程语言技能;中间件专业知识只有少数的专业人员才需要
  • 479. SOA与ESB ESB是一个灵活的用于集成各种应用和各种服务的连接基础架构。企业服务总线能够通过简化应用和服务之间接口的数量、接口大小及接口复杂度等方法使客户的面向服务体系(SOA)更加的强大。企业服务总线提供以下功能: 在服务与服务之间路由消息; 在请求者与服务者之间转换传输协议; 在请求者与服务者之间转换消息格式; 处理来自于各种异构源的业务事件; 企业服务总线使得客户主要关注于核心业务需求,而不再关注用于连接各种程序的IT基础架构。企业服务总线还应该让客户在增加新的服务时或者对已有服务修改时,对原有服务的使用不产生影响或只有微小的影响。
  • 480. 480架构设计实践
  • 481. 481软件设计的步骤: 一、静态设计。 二、模块间的通信及耦合设计。 三、动态设计。 四、模块调整。
  • 482. 482一、静态设计1、按层+高内聚低耦合的原则进行模块划分 1)高内聚低耦合原则(GRASP:高内聚低耦合) 2)按功能分解 3)按业务进行分解 4)以数据转换为中心分解 5)实际运用中的折中 2、划分层次 (架构风格) 1)将模块划入对应的层 2)分层与分区 3)逻辑模块与实体组件的对应关系
  • 483. 483一、静态设计(续)3、为模块进行职责分配 1)信息专家+控制者 2)隔离关注面(GRASP:保护变量、间接模式) 3)低耦合原则 4)适当采用设计模式 4、用设计模式优化核心结构 1)用策略/桥接模式作为中心骨架(多态模式) 2)用工厂/抽象工厂模式进行组装。(创建者模式) 3)用命令模式处理事务
  • 484. 484一、静态设计(续)5、模块结构的常用形式 1) 容器模块 + 控制者 + 功能模块 + 临时构建的小类。(纯虚构模式) a) 单例模式 b) 命令模式 2) 核心模块的接口设计 a) 外观模式 b) 适配器模式 c) 代理模式 d) 调停者模式 3) 变换型模块结构 4) 事务型模块结构
  • 485. 485二、模块间的通信及耦合设计1、组件式程序设计 通常一个领域的专用资产要应用到不相关的领域是比较困难的,组件式开发的首要工作是领域工程,在这个领域内提取可被复用的系统对象,创建可复用资产,开发复用组件。 面向对象技术的特点是通过对象之间的职责分工和高度协作来完成任务。这样的好 处是代码量较少,系统布局合理,重用程度高,但是当对象的个数大量增加的时 候,对象之间的高度耦合的关系将会使得系统变得复杂,难以理解. 2、通讯机制 观察者模式 本地SDK 轮询
  • 486. 486二、模块间的通信及耦合设计(续)3、解耦 1)针对接口编程;(面向对象原则:接口单一原则) 2)增加间接模块;(间接模式) 3)依赖注入 4、设计数据层 1)数据结构选用的设计 2)数据访问层的设计
  • 487. 487三、动态设计1、抽象与统一不同的因素 1)根据业务寻找关键因素 2)向复杂的情况靠齐 2、常用的流程抽象手段 1)依赖注入 / 控制反转 / 依赖倒置(面向对象原则) 2)表格法 3)配置文件 3、逻辑控制: 1)控制者模式 2)信息专家模式 4、消息通知机制 1)MVC模式 2)观察者模式 3)责任链模式 4)中介者模式
  • 488. 488四、模块调整1、调整模块等级 1) 适当封装: 2) 把属性提升为类 3) 将类降为属性 4) 将类提升为组件 2、类细化 1) 李氏替换原则 2) 避免脆弱基类等 3、用设计模式优化,在主体框架上进行调整 1)访问者模式 2)装饰模式
  • 489. 489四、模块调整(续)4、编码时构建适当的动态临时类 1) 命令模式 2) 事务处理类型 3) 纯虚构 5、效率的优化 1) 效率与结构的折中 2) 优化效率的3步骤