《高级软件架构设计课件.ppt》由会员分享,可在线阅读,更多相关《高级软件架构设计课件.ppt(235页珍藏版)》请在三一办公上搜索。
1、.,1,高级软件架构设计,康凯Msn: Mail: ,.,2,目录,第一单元:软件生命周期与软件架构介绍2第二单元:技术架构视图面向对象程序设计原则与模式 24用GRASP模式指导设计27领域模型47面向对象设计的基本原则71第三单元:用UML辅助系统分析与设计103UML简介及常见疑难问题辨析104借鉴RUP的UML建模与分析117第四单元:设计模式与软件设计思想131设计模式132常用的软件架构风格及适用情况分析172SOA 及分层架构设计212第五单元:架构设计实践225,.,3,第一单元:软件生命周期与软件架构介绍,.,4,IT行业的人才结构与软件架构师的定位软件架构师应掌握的知识体系
2、软件架构设计的特点、层次、分类软件架构的主要理论、方向和趋势软件工厂,实现软件开发的产业化,.,5,软件架构师的定位,系统架构师的职责:一、理解系统的业务需求,制定系统的整体框架(包括:技术框架和业务框架)二、对系统框架相关技术和业务进行培训,指导开发人员开发。并解决系统开发、运行中出现的各种问题。系统架构师的目的:对系统的重用、扩展、安全、性能、伸缩性、简洁等做系统级的把握。系统架构师能力要求:一、系统架构相关的知识和经验。二、很强的自学能力、分析能力、解决问题的能力。三、写作、沟通表达、培训。,.,6,角色软件架构师Software Architect定义主导系统全局分析设计和实施、负责软
3、件构架和关键技术决策的角色,.,7,职责领导与协调整个项目中的技术活动(分析、设计和实施等)推动主要的技术决策,并最终表达为软件构架确定和文档化系统的相对构架而言意义重大的方面,包括系统的需求、设计、实施和部署等“视图”确定设计元素的分组以及这些主要分组之间的接口为技术决策提供规则,平衡各类涉众的不同关注点,化解技术风险,并保证相关决定被有效的传达和贯彻理解、评价并接收系统需求评价和确认软件架构的实现,.,8,专业技能技术全面、成熟练达、洞察力强、经验丰富,具备在缺乏完整信息、众多问题交织一团、模糊和矛盾的情况下,迅速抓住问题要害,并做出合理的关键决定的能力。具备战略性和前瞻性思维能力,善于把
4、握全局,能够在更高抽象级别上进行思考。对项目开发涉及的所有问题领域都有经验,包括彻底地理解项目需求,开展分析设计之类软件工程活动等。具备领导素质,以在各小组之间推进技术工作,并在项目压力下做出牢靠的关键决策。拥有优秀的沟通能力,用以进行说服、鼓励和指导等活动,并赢得项目成员的信任。,.,9,以目标导向和主动的方式来不带任何感情色彩地关注项目结果,构架师应当是项目背后的技术推动力,而非构想者或梦想家(追求完美)精通构架设计的理论、实践和工具,并掌握多种参考构架、主要的可重用构架机制和模式。具备系统设计员的所有技能,但涉及面更广、抽象级别更高。,.,10,软件架构师的知识体系,软件架构师作为整个软
5、件系统结构的总设计师,其知识体系、技能和经验决定了软件系统的可靠性、安全性、可维护性、可扩展性和可移植性等方面的性能。因此一个优秀的软件架构师必须具备相当丰富的知识、技能和经验。通过对比软件架构师和系统分析师在软件开发中的职责和角色,不难发现软件架构师与系统分析师所必需的知识体系也是不尽相同的,系统分析师的主要职责是在需求分析、开发管理、运行维护等方面,而软件架构师的重点工作是在架构与设计这两个关键环节上。因此在系统分析师必须具备的知识体系中对系统的构架与设计等方面知识体系的要求就相对低些;而软件架构师在需求分析、项目管理、运行维护等方面知识的要求也就相对低些。,.,11,成为一名合格的软件架
6、构师必须具备的知识信息系统综合知识体系软件架构知识体系,.,12,?,MFC,MSF,MOF,RUP,J2EE,Spring,SOA,JUnit,ORM,.NetMVC,UML,XML,Corba,MDA,MDD,Web-ServiceRSS,Web2.0,AJAX,Serverlet,HibernateIOC, AOPRuby On RailsRupBPELWorkflow EngineLBSOracleCMMIMQ,.,13,软件架构师在干什么?,思考、思考、再思考深入理解、准确把握建设的业务需求分析所有可见的问题、障碍、风险充分参考已有的成功方案,降低风险交流、讨论、博弈、质疑对构思中的
7、方案不断提出质疑,避免漏洞广泛听取各层面的意见,开拓思路反复质疑、逐步完善已有的设计构思在动手实现之前验证设计方案的正确性,.,14,软件架构师的知识结构,软件知识最好要有系统开发全过程经验。对 IT 建设生命周期各个环节有深入了解,包括:系统/模块逻辑设计、物理设计、代码开发、项目管理、测试、发布、运行维护等。深入掌握1-2种主流技术平台上开发系统的方法。了解多种应用系统的结构。了解架构设计领域的主要理论、流派、框架。,.,15,软件架构师的知识结构,业务知识深入了解系统建设的业务需求。了解系统的非功能需求和运行维护需求。了解企业 IT 公共设施、网络环境、外部系统。,.,16,软件架构师的
8、思维方式,基于框架的思维架构设计的层次(Enterprise, Application, etc)IT 的生命周期(What, Why, Where, How, When, etc)成功经验以及方法论的指导合理把握技术细节把握各个层次应有的内容合理忽略不应有的技术细节,.,17,软件架构师的思维方式,风险管理意识采用成功经验、避免不应有的风险多方位的开放思维多维度、多方向、包容性、避免排他性分析、质疑、抽象、归纳没有绝对好的架构设计,只有相对优秀的方案,.,18,信息系统综合知识体系,(1)计算机系统综合知识:包括计算机组成与体系结构、嵌入式系统和操作系统等方面的知识。(2)系统配置和方法:包
9、括系统配置技术和系统性能等方面的知识。(3)典型系统应用:包括网络应用、数据库应用和多媒体系统等方面的知识。(4)系统开发:包括程序设计语言、软件开发方法、需求分析和设计方法、测试评审方法、开发管理、应用系统构建、系统审计、外部资源使用和基于中间件的开发等方面的知识。(5)安全性和可靠性技术:包括数据安全与保密、防闯入和防病毒、容错技术、可靠性模型与分析技术、系统可靠性、安全规章和保护私有信息规则等方面的知识。,.,19,(6)标准化:包括标准化的基础知识、标准化分级、编码标准、数据交换标准、软件工程标准、信息安全标准、基于构件的软件标准和标准化组织机构等方面的知识。(7)信息化基础:包括政府
10、信息化与电子政务、企业信息化与电子商务、信息化的有关的法律和规定等方面的知识。(8)数学和英语:至少具有大学以上的数学和英语基础知识。,.,20,软件架构知识体系,(1)系统计划:包括项目的提出和可行性分析、系统方案的制定、评价和改进、新旧系统的分析与比较、现有软、硬件和数据资源的有效利用等。(2)软件架构设计:包括软件架构的概念、软件架构与设计、架构风格、特定领域的架构风格、基于架构的软件开发方法、架构评估、软件产品线和系统演化等。(3)设计模式:包括设计模式的概念、组成、分类和实现、模式和软件架构的关系等。(4)系统设计:包括处理流程设计、人机界面设计、文件与存储设计、数据库设计、网络应用
11、系统的设计、系统运行环境的集成与设计、中间件与应用服务器、性能设计与性能评估等。(5)软件建模:包括定义问题与归结模型、结构化系统建模与数据流图、面向对象系统建模、数据库建模和逆向工程等。,.,21,(6)分布式系统设计:包括分布式通信协议的设计、基于对象与web的分布式设计、基于消息和协同的分布式设计和异构分布式系统的互操作性设计等。(7)嵌入式系统设计:包括实施任务调度和多任务设计、中断处理和异常处理、嵌入式系统开发设计等。(8)系统可靠性分析与设计:包括系统故障模型和可靠性模型、系统的可靠性分析与可靠度计算、提高系统可靠性的措施、系统的故障对策和系统的备份与恢复等。(9)系统的安全性和保
12、密性设计:包括系统的访问控制技术、数据的完整性、数据与文件的加密、通信的安全和系统的安全设计等。(10)复杂架构设计:包括操作系统的架构、编译器的架构和大型基础库的架构等。,.,22,软件架构师的任职条件,根据软件架构师的职责和角色定位,以及知识体系,从实践的角度考虑,合格的软件架构师应该具有以下能力和经验:(1)具有8年以上的软件项目开发实际工作经验,其中至少有3年以上的代码编写工作经验,4年以上的基于面向对象和构件开发方法的软件产品设计经验。 (2)具有5个以上大中型开发项目的总体规划、方案设计经验,有大中型应用系统开发和实施的成功案例。(3)对相关的技术标准有深刻的认识,对软件工程标准和
13、规范有良好的把握。 (4)对.Net或Java技术及整个解决方案有深刻的理解及熟练的应用,精通Web Service,熟练掌握流行的架构。,.,23,(5)对设计模式有深刻的理解,并能在此基础上设计出适合产品特性和质量属性的框架。(6)具有面向对象的分析、设计和开发能力,精通UML和XML,能熟练使用Rational Rose、PowerDesigner等工具进行设计。 (7)具有良好的团队意识和协作精神,有较强的沟通能力和书面表达能力。(8)具有旺盛的精力和学习能力,能快速掌握新技术和新方法。,.,24,第二单元:技术架构视图面向对象程序设计原则与模式,.,25,.,26,.,27,用GRA
14、SP模式指导设计,.,28,.,29,.,30,.,31,.,32,.,33,.,34,.,35,.,36,.,37,.,38,.,39,.,40,.,41,.,42,.,43,.,44,.,45,.,46,.,47,领域模型,.,48,层次结构领域模型从EJB到轻量级框架,.,49,层次结构,表现层(present)业务层业务层外观业务层核心领域对象管理/服务/仓库层领域对象层持久层数据访问层数据库,.,50,领域模型中的各种角色:实体-有唯一的标识,并且要有属性和行为(非GET/SET),添加了行为,使其具有生命力。往往在设计时,实体的形为最难决断。为确定行为,我们必须识别它们的责任和协作
15、。类的责任是指该类要做、知道、或决定的一切,由一个或多个方法完成。类中有属性和关联,协作就是为完成自己的责任所调用其它关联类。值对象-没有标识没有行为。如Address类。工厂-定义创建实体的方法,封装实例化对象并将一些关联对象注入。仓库(repository)管理实体的集合,主要有查找和删除实体的方法.实现类可以调用执久化层(如Hibernate,Ibatis)服务(Service) ,实现整个应用程序的工作流(workflow)。服务包含那些无法指派的单个实体的行为,由作用于多个对象方法组成。如可以调用repository查找到实体对象,然后委派给这些对象。服务和facade很像,但不一样
16、,它不处理以下事情:1)执行事务。2)收集返回给表现层的数据。3)脱钩对象。4)其它事情。服务可以说是业务的协调者,业务逻辑可以分散到实体对象中。,.,51,领域模型,失血模型贫血模型充血模型胀血模型,.,52,失血模型,DO只有属性及其getter/setter方法,没有任何业务逻辑。缺点:行为与数据分离,很多情况导致维护与理解困难。,.,53,贫血模型,DO包含不依赖于持久化的领域逻辑;依赖持久化的领域逻辑归入Service层。Service(业务逻辑,事务封装) DAODO优点:各层单向依赖,结构清楚,易于实现和维护。设计简单易行,底层模型非常稳定。缺点:DO部分的持久化逻辑被放入Ser
17、vice层,不够OO。Service层过重。,.,54,充血模型,与贫血模型类似,不同处在于如何划分业务逻辑:绝大多业务逻辑都应该放在DO里(包括持久化逻辑),而Service层很薄,仅仅封装事务和少量逻辑,不和DAO层打交道。Service(事务封装)DO DAO优点:符合OOService层很薄,只充当Facade的角色,不和DAO打交道。,.,55,缺点:DAO和DO双向依赖。如何划分Service层逻辑和Domain层逻辑没有确定的规则,取决与设计人员自己的理解。Service层封装事务,须对所有的DO逻辑提供相应的事务封装方法,造成重定义所有的Domain logic。Service
18、的事务化封装的意义等于把OO的Domain logic转换为过程的Service 事务脚本。充血模型在domain层实现的OO在Service层又变成了面向过程。,.,56,胀血模型,取消Service层,只剩下DO和DAO层,在DO的domain logic上面封装事务。DO(事务封装,业务逻辑)DAO(RoR甚至合并为一层) 优点:分层简化符合OO缺点:service逻辑也强行放入DO ,引起了DO不稳定。DO暴露给web层过多的信息,可能引起不必要的耦合。,.,57,原则:业务对象封装了内在的业务逻辑,而应用服务封装了外在于业务对象的业务逻辑。,.,58,EJB到轻量级框架,EJBPOJ
19、O (业务逻辑) + 轻量级框架(Hibernate、JDO、iBATIS(持久化)、Spring(事务管理、安全),.,59,EJB,EJB:编写分布式业务应用程序的Java标准架构。提供大量服务:声明型事务, EIB容器自动启动、提交和回滚事务。业务逻辑能参与由远程客户发起的分布式事务。提供声明型安全,大部分情况下不再摇要编写安全代码求(bean部署描述符里的条目指定准可以防问某个具体bean)。,.,60,例:在两个账号间进行转账的服务。,.,61,POJO(业务逻辑) +Hiibernate、JDO、iBATIS(持久化)、Spring(事务管理、安全)。 典型的EJB方法 POJO方
20、法 组织 过程式业务逻辑 面向对象设计 实现 基于EJB POJO 数据库访问 JDBC/SQL或实体bean 持久层框架 返回给表示层的数据DTO 业务对象 事务管理 EJB容器管理的事务 Spring框架 应用程序组装 显式的JNDI查询 依赖注入 ,.,62,新设计是面向对象、基于POJO, 而非基于EJB的过程式。它使用构建在JDBC上的持久层框架来访问数据库, 并不直接使用JDBC。业务逻辑由POJO facade而非会话bean进行封装。事务由Spring框架而非EJB容器进行管理。业务逻辑向表现层返回实际的业务对象,而不是DTO。应用程序通过将组件的依赖作为setter或构造子参
21、数传入来进行组装,而不是之前采用Java命名和目录接口(JNDI )查询的组件。由于该设计面向对象,并采用上述轻量级技术,因此较之前看到的EJB版本对开发人员要友好。,.,63,基于轻量级框架设计的好处是,它提供事务和持久化时并不要求应用程序类实现任何特殊接口。甚至当应用程序的类需要运行在事务里或是持久的时候,它们仍是POJO,设计者可以继续享受POJO的种种好处。,.,64,.,65,面向对象的优点:整个设计更易理解和维护。它并不是一个无所不能的大型类, 而是由大量小类组成, 每个类只共有若干职责。此外,诸如Account、BankingTransaction和OverdraftPolicy
22、类都与现实世界的概念对应, 因此易于理解。其次,面向对象设汁也更易测试: 所有类都能并且应当进行独立的测试。EJB只能通过调用它的public 方法如Transter进行测试,难度大。(只能测试p-blic方法暴露的复杂功能,无法测试其中简单的部分)。面向对象象设计更易扩展, 它可使用设计模式,如Stategy和Template等。EJB风格的过程式设计往往需耍修改核心代码。,.,66,不适合用面向对象的场合:大量数据集合的关系操作。以数据库为中心的管理程序 :这个领域所对应的现实世界是一个面向关系的世界,表与表的关联体现的是彼此的业务关系。 复杂的SQL固然不好维护,但业务真是复杂到简单的S
23、QL都难以描述的程度,采用面向对象描述则更加困难,维护也更困难,同时还损失了效率。比较大的事务。性能要求高的地方。(直接用Sql或者存储过程;牺牲可维护性和可复用性)上层流程。,.,67,消除DTO,.,68,部署POJO程序,.,69,.,70,.,71,面向对象设计的基本原则,.,72,.,73,liskov替换原则(LSP),.,74,子类型必须能够替换掉其基类型,问题的根源是关于行为的:基类中有的行为在子类中不存在或不适当。IS A的本质是指行为的一致,而不是生活中的语言。违反了LSP原则的本质是派生类的行为与基类中的不一致。如果违反了LSP原则,常会导致在运行时的类型判断(RTTI)
24、违反OCP原则。例如:函数A的参数是基类型,调用时传递的对象是子类型,正常情况下,增加子类型都不会影响到函数A的,如果违反了LSP,则函数A必须小心的判断传进来的具体类型,否则就会出错,这就已经违反了OCP原则。,.,75,违反LSP导致违反OCP的简单例子,.,76,改善,.,77,例:会议管理系统,.,78,例:GUI对象,假定一个Component代表一个GUI对象,如按钮或者文本框等。,.,79,.,80,.,81,改善2,.,82,例,.,83,接口隔离原则(ISP),康凯,.,84,例,.,85,使用委托分离接口,.,86,使用多重继承分离接口,.,87,内接口与外接口,.,88,
25、普通接口与智能接口,.,89,软件系统坏死的症状,.,90,“Copy”程序,一个从键盘读入字符并输出到打印机的程序。void Copy()int c;while (c = RdKbd() != EOF)WrtPtr(c);,.,91,需求在变化,用户希望Copy程序能从纸带读入机中读入信息。现实中的约束不能改变接口Copy程序的第一次修改结果bool ptFlag = false;/remember to reset this flagvoid Copy()int c;while (c = (ptFlag ? Rdpt() : Rdkbd() != EOF)WrtPtr(c);,.,92,需
26、求在变化2,客户希望Copy程序有时可以输出到纸带穿孔机上。/Copy程序的第二次修改结果bool ptFlag = false;bool punchFlag = false;/remember to reset these flagvoid Copy()int c;while (c = (ptFlag ? Rdpt() : Rdkbd() != EOF)punchFlag ? WrtPunch(c) : WrtPtr(c);,.,93,依赖倒置原则(DIP),康凯,.,94,相关概念,关于解耦依赖倒置(DIP)控制反转(IoC)依赖注入(DI)服务定位器(SL)有些是手段,有些是原则,不过其
27、间的差异并不太重要,更重要的是其共同点:其根本目标都是解除依赖,将组件的配置与使用分离开。其它名词服务组件框架类库应用程序,.,95,接口和实现分离,面向过程的接口与实现分离,.,96,.,97,.,98,.,99,电影清单的例子,一个组件,用于提供一份电影清单,清单上列出的影片都是由一位特定的导演执导的。class MovieLister . public Movie moviesDirectedBy(String arg) List allMovies = finder.findAll(); for (Iterator it = allMovies.iterator(); it.hasNe
28、xt();) Movie movie = (Movie) it.next(); if (!movie.getDirector().equals(arg) it.remove(); return (Movie) allMovies.toArray(new MovieallMovies.size(); ,.,100,其中真正想要考察的是如何将MovieLister对象与特定的finder对象连接起来。要求:我们希望moviesDirectedBy方法完全不依赖于影片的实际存储方式。这个方法只能引用一个finder对象,而finder对象必须知道如何实现findAll的细节。给finder定义一个接
29、口: public interface MovieFinder List findAll(); 当要实际寻找影片时,就必须涉及到MovieFinder 的某个具体子类。在这里,我把“涉及具体子类”的代码放在MovieLister 类的构造函数中。,.,101,对抗变化,从一个逗号分隔的文本文件中获得影片列表。class MovieLister. private MovieFinder finder; public MovieLister() finder = new ColonDelimitedMovieFinder(movies1.txt); 对抗变化:文件清单的名字更改:可以从一个配置文件
30、获得文件名。如果用SQL 数据库、XML 文件、web service,或者另一种格式的文本文件来存储影片清单:用另一个具体的类来获取数据。该类从MovieFinder接口派生即可。创建合适的MovieFinder派生类的实例:不能对抗此变化。,.,102,配置文件,设定配置文件:XML 文件是比较理想的配置方式。movies1.txt,.,103,第三单元:用UML辅助系统分析与设计,.,104,UML简介及常见疑难问题辨析,.,105,UML用来描述模型的内容有3种,分别是事物( Things )、关系Relationships )和图( Diagrams )。,.,106,UML中的关系
31、,UML中的关系(Relationships )主要包括4种:1、关联关系2、依赖( Dependency)关系3、泛化( Generalization )关系4、实现( Realization )关系,.,107,一些常见问题辨析,类的层次结构表示属性与聚合关联角色关联类,.,108,层次结构,.,109,领域建模重数,.,110,细化类模型,.,111,关联角色,关联角色名出现在关联终端的旁边。当仅仅使用关联名不足够表达清楚后,可以用关联角色名来加强表达。可以把每个名称都当成伪属性,关联角色名提供了一种可以遍历关联的方法。,.,112,在创建类图时,应该为使用正确的角色名,而不是为每个引用
32、引入一个独立的类。因为角色名可以区分对象,所以附在一个类上的关联名必须唯一(可以把角色名想象成类的伪属性)。同样,角色名不应该与类的属性名重复。,.,113,关联类,正如可以用属性描述类的对象一样,也可以用属性来描述关联的链接,可以把这样的一组属性组成关联类。,.,114,Actor的一些注意事项,包括人与系统,总是外部的。定义了边界。Actor是“角色”,不是特定的人或特定的事。要有恰当的名字。可以泛化不是可有可无的东西。另一方面它可以划分系统与外部实体的界限,是系统设计的起点。,.,115,用例的一些注意事项,是需求分析的第一步。用户首先关心功能。用例是对一个系统或一个应用的一种单一的使用
33、方式所作的描述,是关于单个活动者在与系统对话中所执行的处理行为的陈述序列。不是事件流。不是需求规格说明,但反映了主要的功能性需求。识别用例最好是从分析流开始。每个用例都是独立的。用例名用动宾结构描述,不要写成一个名词。用例是分层的,一般而言,高层/中层用例更有实际意义。不要将不同的用例混合在一起。用例与编码:低层的用例可以辅助编码,高层的不能。扩展用例:基础用例不必知道扩展用例的细节,只提供扩展点。,.,116,仓库信息系统的用例图,.,117,借鉴RUP的UML建模与分析,.,118,全局分析,全局分析侧重于定义拟建系统所采用的构架以及影响构架的要素。全局分析充分利用相似或问题中的经验,避免
34、在确定构架上浪费人力和物力。选用架构模式识别关键抽象:寻找那些无论在问题域或方案领域都具有普遍意义的概念点。标识分析机制:将那些问题领域(应用逻辑)没有直接关联的计算机概念及相应的复杂行为表述为支撑分析工作的“占位符”。选定分析局部:针对拟建系统的整体架构,找出那些蕴含相对高风险的局部作为工作内容。,.,119,全局分析,常见的分析机制留存分布式处理安全性进程间通信消息路由进程控制与同步交易事务管理信息交换信息的冗余错误检测、处理和报告数据格式转换,.,120,局部分析,提取分析类转述需求场景整理分析类,.,121,局部分析,分析类的类型划分众多实践表明,如果立足于软件功能需求,拟建系统往往在
35、三个维度易于发生变化:一、拟建系统和外部要素之间交互的边界;第二,拟建系统要记录和维护的信息;第三,拟建系统在运行中的控制逻辑。通常按照这三个变化因素的维度,将分析类划分为三种类型:边界类、控制类、实体类,系统边界,Use Case行为协调,基本信息,.,122,局部分析,.,123,局部分析,.,124,局部分析,.,125,局部分析,整理分析类分析类是这样的类:它代表问题域中的简洁抽象;分析类映射到真实世界的业务概念(并且据此仔细命名)。 问题域是首先产生软件系统(以及从此而来的软件开发活动)需求的域。通常,这是特定的业务领域,如在线销售或者客户关系管理。然而,务必注意,问题域可能根本不是
36、任何特定业务活动,但 是可能产生需要软件在其上运转的一块物理硬件这是嵌入式系统。基本上,所有业务软件开发服 务于某种业务需求,自动化一个已有业务过程或者开发具有有意义的软件组件的新产品。,.,126,分析类的职责,职责是类和它的客户之间的契约或者是类对它的客户的义务。本质上,职责是类提供给其他类的 服务。分析类具有直接同类(由它的名称所表达)的目的相一致的以及直接同该类正在建模的真实世 界“事物”相一致的非常内聚的职责集合,这一点是至关重要的。例如ShoppingBasket示例,你将期 望该类具有如下职责:向购物篮添加商品;从购物篮删除商品;显示购物篮中的商品。 这是内聚的职责集合,一切都是
37、为了维护客户选择的商品集合。它是内聚的,因为所有的职责都朝着相同的目标维护客户已经选择的商品集合。实际上,我们能够把这些职责概括为非常高级层 次的职责“维护购物篮”。同样,向 ShoppingBasket 中添加如下职责:,.,127,分析类的职责,验证信用卡;接收付款;打印收据。 但这些职责似乎同购物篮的目的或直觉语法不匹配。它们不是内聚的,显然应该赋予其他什么类 可能是类CreditCardCompany、类Checkout以及类ReceiptPrinter。把职责适当地分配给分析类 以最大化每个类中的内聚性,是很重要的。最后,良好的类与其他类之间具有最低数目的耦合。我们以给定类与其他类具
38、有关系的数目来度量类间的耦合度。类间职责的平均分布趋向于产生低耦合度。把控制或职责局限于单一的类趋向于增 加到该类的耦合度。,.,128,分析类经验法则,以下是创建形式良好的分析类的一些经验法则。每个类大约35个职责典型地,类应该保持尽可能简单,这通常限制类能够支持的35个职 责的数目。先前ShoppingBasket 的示例是带有小的和可管理数目职责的专注的类的好的示例。不存在独立的类好的OO分析和设计的精华是,类相互协作让用户受益。同样,每个类应该 同小部分类协作以提供所期望的功能。类可以把它们的一些职责托付给专注于特定功能的其他“辅助”类。当心很多非常小的类 有时很难取得正确的平衡。如果
39、模型看起来具有大量的非常小的类, 每个类都具有一个或者两个职责,那么你应该仔细查看以把一些小的类整合成更大的类。,.,129,当心少数几个非常庞大的类上述的反面是,模型具有很少的类,但每个类都是具有职责数 量(5)的庞大的类。解决问题的策略是依次查看这些类,看看是否每个类都能够被分解成为 两个或者多个能够承担恰当数目职责的、更小的类。当心“伪类”伪类其实是一般的过程函数,它伪装成类。当心万能类 存在似乎能够承担任何工作的类。看看名称为“system”和“controller”的 类!处理这个问题的策略是看看万能类的职责是否能够分解成内聚的子集。如果是,每个这些 内聚职责集合可能独立成类。这些更
40、小的类协作以实现由原始万能类所提供的行为。,.,130,分析类经验法则,避免深度继承树设计良好的继承层次的本质是继承层次中每个抽象层次应该具有良好定义 的目的。容易添加很多实际上不能服务于任何目的层次。实际上,通常的错误是使用继承来实 现一种功能分解,其中每个抽象层次恰有一个职责! 无论从哪个方面来讲,这都是无意义的,是会导致复杂的、难以理解的模型。在分析中,类代表业务事物,而业务事物趋向于形成更宽(不超过三层)的继承层次。 我们把三层或者更多层次的继承认为是“深度”继承。,.,131,第四单元:设计模式与软件设计思想,.,132,设计模式,.,133,设计模式在实际开发中的运用,复用现有的、
41、高质量的、针对常见的重复出现问题的解决方案。建立通用的术语以改善团队内部的沟通。将思考转移到更高的视角。判断是否拥有正确的设计,而不仅仅使一个可以运行的设计。改善代码的可修改性。发现“庞大的继承体系”的替代方案。,.,134,GoF中的模式分类,.,135,设计模式的特点,设计模式最根本的意图是适应需求变化隔离变化的部分与不变的部分,将之封装起来。针对接口编程,而不要针对实现编程达成高内聚合低耦合,提高复用提倡优先使用聚合,而不是继承,.,136,例,一个日志记录工具。目前需要提供一个日志API,提供客户方便地调用。该日志要求被记录到指定的文本文件中,记录的内容属于字符串类型,其值由客户提供。
42、可以容易地定义一个日志对象。public class Logpublic void Write(string target, string log) /实现内容; 当客户需要调用日志的功能时,可以创建日志对象,完成日志的记录:Log log = new Log();log.Write(“error.log”, “log”);,.,137,.,138,.,139,例,我们需要设计一个数据库组件,它能够访问Sql Server数据库。如果用ADO.Net,需要使用如下的对象:SqlConnection, SqlCommand, SqlDataAdapter等。不用模式的做法:可以直接创建这些对象:
43、SqlConnection connection = new SqlConnection(strConnection);SqlCommand command = new SqlCommand(connection);SqlDataAdapter adapter = new SqlDataAdapter();,.,140,Connection对象:,.,141,.,142,策略(Strategy)模式,.,143,练习,下面是一堆杂乱的类与接口:动作冒险游戏。包括代表游戏角色的类,以及武器行为的类。每个角色一次只能使用一个武器,但是可以在游戏的过程中换武器。任务:1.安排类。2.找出一个抽象类、
44、一个接口、以及八个类。3.在类之间画箭头。 A.继承。B.实现接口。C.有一个关系。4. 把setWeapon() 方法放到正确的类中。,.,144,原始的类与接口,.,145,例:电子零售系统,该电子零售系统必须处理来自不同国家的销售定单。例如在美国与加拿大。需求如下:,.,146,分析矩阵,.,147,桥接(Bridge)模式,.,148,意图“将抽象部分与它的实现部分分离,使它们都可以独立地变化”。抽象部分是指“不同的事物在概念层次上的联系”。分离是指“让各部分的行为各自独立,或至少显式指出关联”。,.,149,例,通过引入一个Rectangle抽象类,利用了这一事实:不同的Rectan
45、gle派生类之间唯一的差异是如何实现drawLine方法。V1Rectangle类的实现方式是:保存一个DP1对象的引用,使用DP1对象的draw_a_line方法。V2Rectangle类的实现方法是:保存一个DP2对象的引用,使用这个对象的drawline方法。,.,150,需求变化,用户要求支持另一种形状圆形。,.,151,识别变化,首先识别出“什么在发生变化”。在上述的例子中,变化点是“不同类型的形状”和“不同类型的画图程序”。共同的“概念”则是“形状”和“画图程序”。 用Shape类来封装拥有的形状的概念。形状有责任知道如何画自己。Drawing对象负责画线和圆。,.,152,描述变
46、化,下一步是描述出现的特定变化:Shape类拥有矩形和圆形。画图程序分别拥有一个基于DP1的对象(V1Drawing)和一个基于DP2的对象(V2Drawing)。,.,153,桥接模式,.,154,观察者(observer)模式,康凯,.,155,.,156,命令(command)模式,康凯,.,157,意图将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。别名动作(Action),事务(Transaction)动机有时必须向某对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息。例如,用户界面工具箱包括按钮和菜单这
47、样的对象,它们执行请求响应用户输入。但工具箱不能显式的在按钮或菜单中实现该请求,因为只有使用工具箱的应用知道该由哪个对象做哪个操作。而工具箱的设计者无法知道请求的接受者或执行的操作。命令模式通过将请求本身变成一个对象来使工具箱对象可向未指定的应用对象提出请求。这个对象可被存储并像其他的对象一样被传递。这一模式的关键是一个抽象的Command类。,.,158,例子,.,159,结构,.,160,其它设计模式,.,161,VISITOR模式该系列中的模式如下:VISlTOR模式ACYCLIC VISITOR模式DECORATOR模式EXTENSION OBJECT模式,.,162,例,是一个常见的
48、问题:例如,有一个Modem对象的层次结构。基类中有所有调制解调器的公共方法。派生类代表不同调制解调器类型的驱动程序。,.,163,问题,假设需要向该层次结构中增加一个新方法confrgureForUnix。使之可在UNIX下工作。在每个调制解调器派生类中该函数的实现都不相同。(假设每个不同的调制解调器在UNIX中都有自己独特的配置方法和行为特征)。问题:直接增加configureForUnix方法其实回避了一个问题:对于Windows如何处理? MacOS? Linux? 必须针对所使用的每一种新操作系统都要向Modem层次结构中增加一个新方法? 这种做法是丑陋的:我们永远无法封闭Modem
49、接口。每当出现一种新操作系统时, 就必须更改该接口并重新部署所有的调制解调器软件。,.,164,VISITOR模式的结构,.,165,VISITOR组合模式,VISTTOR模式的一个非常常见的应用是,遍历大量的数据结构并产生报表。这使得数据结构对象中不含有任何产生报表的代码。如果想增加新报表,只需增加新的访问者,而不需要更改数据结构中的代码。这意味着报表可以被放置在不同的组件中,并且仅被那些需要它们的客户单独使用。,.,166,例:报表生成器,一个表示材料单的简单数据结构。从该数据结构可以生成无数的报表。两个可以统计的量:成本;数量例如可以生成一张一个组合件总成本的报表,或者生成一张列出了一个
50、组合件中所有零件的报表。,.,167,VlSITOR模式的解决方法,.,168,其它模式,问题:考虑前面的Modem层次结构。假设有一个具有很多使用者的应用程序。每个使用者都可以坐在他的计算机前,要求系统使用该计算机的调制解调器呼叫另一台计算机。有些用户希望听到拨号声,有些用户则希望他们的调制解调器保持安静。,.,169,DECORATOR模式,DECORATOR模式通过创建一个名为LoudDialModem的全新类来解决这个问题。LoudDialModem派生自Modem ,并且委托给一个它包含的Modem实例。它捕获对dial函数的调用并在委托前把音量设高。,.,170,多个Decorat