2015-OO-设计模式(1)(2)
- 格式:ppt
- 大小:1.90 MB
- 文档页数:74
OO设计原则(一)好的作品来源于好的设计,那么好的设计又来自于哪里?且看OO设计中最常用的几大原则了。
单一职责原则(SPR)核心思想:一类不二用,一个类,最好只要专注于一件事,且只有一个引起它变化的原因。
它强调的是对职责的分离,是模块间保持低耦合,高聚合的一个关键。
下面就来看一下对比,亲身感受一下SPR。
对数据库的操作经常是违背该设计原则的重灾区。
比如当只有符合权限的用户才能对数据进行添加操作时,我们一般会在对数据操作之前对用户身份进行判别。
很多时候我们就会直接写代码如下:Class DBManager{pubic void Add(){If(Permission(userName) == true){Console.WriteLine(“用户可以对数据进行添加”);}public bool Permission(string UserName){//处理权限判断}}}初看可能并没有什么问题,但是仔细看就会发现,在以上设计中,DBManager类将对数据库的操作和对用户权限的判别封装在一起实现。
这样子,现在可能没多大问题,但如果以后对权限的设置规则发生改变的话,那可就有得玩了,你可能得把所有的数据库操作逻辑都重新修改。
这时,你就应该想到要遵循SPR设计原则对其修改了。
修改后如下://用接口定义有哪些操作public interface IDB Operation{Add();bool Delete();……}//而DBManager只关注数据的操作public class DBManager: IDB Operation{public void Add(){//执行数据增加}}通过以上重构,实现了职责的分离,DBManager类在此只关注数据操作,而将权限判断逻辑交给下面的DBManagerProxy代理类来完成。
public class DBManagerProxy: IDB Operation{private IDB Operation m_dbManager;public DBManagerProxy(IDB Operation dbOperation){m_dbManager = dbOperation;}public bool Permission(string UserName){//处理权限判断}public void Add(){if(Permission(userName) == true){m_dbManager.Add();}}}通过此代理,我们将数据操作和权限判断两个职责分离了开来,而实际的数据操作则由DBManager来完成,这样你会发现客户端的调用也将变得相当简单:public class DBClient{public static void Main(){IDB Operation DBManager = new DBManagerProxy(new DBManager(userName))DBManager.Add();}}自此,DBManger将只有数据操作的变更这个会引起变化的原因,而权限的变更和修改则就不会对anger造成任何影响了。
偶然发现这个社区,发一篇较早前写的文章。
若大家觉得还有点帮助作用,我将继续发后续几篇。
我发现,在OO和UML几乎一统天下的今天,仍有很多系统分析员对OO和UML一知半解,甚至包括很多已经使用了很久UM L的系统分析员。
于是打算写一个系列文章,将多年来的工作经验做一个总结。
对初学者起个启蒙作用,也希望抛砖引喻,与各路大虾共同探讨,共同提高。
这个系列文章将以我对OO和系统分析的理解为主,从UM L基础开始,阐述面向对象的需求分析方法,过程,并以RUP 为例,阐述如何将OO过程与软件过程有机结合在一起,做一个真正OO应用。
好了,今天是第一篇。
想得很远,真希望我能坚持下去,呵呵用例是什么?其原始英文是usecase,直译过来就成了用例。
这也是一个比较贴切的叫法了,从字面的直接理解就是使用的例子。
另一种比较流行的定义是用例就是与使用者(actor)交互的,并且给使用者提供可观测的有意义的结果的一系列活动的集合。
这个定义还是比较费解的,笔者在众多应聘者中发现很多使用用例来做需求的系统分析员,有的已经使用了两年以上,但仍不能把握用例的本质,虽然他们号称精通UML。
最具普遍意义的理解错误是认为用例就是功能的划分和描述,认为一个用例就是一个功能点。
在这种理解下,用例变成了仅仅是较早前需求中功能框图的翻版,很多人用用例来划分子系统,功能模块和功能点。
如果这样,用例根本没有存在的必要。
有意思的是,造成这种理解错误的相当一部分原因却是因为对OO思想的理解不够深入,本质上说,把用例当成功能点的系统分析员脑子里还是面向过程的那一套思想,虽然他们在使用OO的工具,OO的语言,号称在做面向对象的开发,但过程的影子还没有从他们脑子里彻底抹去。
如果用例不是功能的话,它是什么呢?从定义上说,能给使用者提供一个执行结果的活动,不就是功能吗?我的回答是:错!功能是计算机术语,它是用来描述计算机的,而非定义需求的术语。
功能实际描述的是输入-->计算-->输出。
OO设计原则在软件软件系统中,一个模块设计得好不好的最主要、最重要的标志,就是该模块在多大程度上将自己的内部数据和其他与实现有关的细节隐藏起来。
一个设计得好的模块可以将它所有的实现细节隐藏起来,彻底地将提供给外界的API和自己的实现分隔开来。
这样一来,模块与模块之间就可以仅仅通过彼此的API相互通信,而不理会模块内部的工作细节。
OO设计根本的指导原则是提高可维护性和可复用性。
这些原则主要有:1. 开闭原则一个软件实体应该对扩展开放,对修改关闭。
在设计一个模块的时候,就当使这个模块可以在不被修改的前提下被扩展。
换言之,就当可以在不必修改源代码的情况下改变这个模块的行为。
如何做到既不修改,又可以扩展?解决问题的关键在于抽象化:在Java语言里,可以给出一个或多个抽象Java类或Java接口,规定出所有的具体类必须提供的方法特征作为系统设计的抽象层。
这个抽象层预见了所有的可能扩展,因此,在任何扩展情况下都不会改变。
这就使得系统的抽象层不需要修改,从而满足了—对修改关闭。
同时,由于从抽象层导出一个或多个新的具体类可以改变系统的行为,因此系统的设计对扩展是开放的。
开闭原则实际上是对“对可变性的封闭原则“:找到一个系统的可变因素,将之封装起来。
这个原则意昧着两点:1) 一个可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里面。
同一种可变性的不同表象意昧着同一个继承等级结构中的具体子类。
继承就当被看作是封装变化的方法,而不应当被认为是从一般的对象生成特殊对象的方法。
2) 一种可变性不应当与另一种可变性混合在一起。
(所有类图的继承结构一般不会超过两层,不然就意昧着将两种不同的可变性混合在了一起。
)开闭原则是总的原则,其它几条是开闭原则的手段和工具。
2. 依赖倒转原则依赖倒转原则讲的是:要依赖于抽象,不要信赖于实现。
开闭原则是目标,而达到这一目标的手段是依赖倒转原则。
抽象层次包含的是应用系统的商务逻辑和宏观的、对整个系统来说重要的战略性决定,是必然性的体现;而具体层次则含有一些次要的与实现有关的算法和逻辑,以及战术性的决定,带有相当大的偶然性选择。
深圳大学课程教学大纲课程编号: 1500300001 课程名称: 软件体系结构开课院系: 计算机与软件学院网络软件工程系制订(修订)人: 毛斐巧审核人: 批准人:2015年3月17日制(修)订课程名称: 软件体系结构英文名称: Software Architecture总学时: 54 其中:实验课 18学时学分: 2.5先修课程: 面向对象系统分析与设计、统一建模语言教材:刘伟.设计模式,清华大学出版社,2011参考教材:[1]《设计模式实训教程》,刘伟著,清华大学出版社课程性质: □综合必修□专业必修■专业选修□全校公选教学目标:开设本课程的目的是为建立一个基于模式的软件体系结构概念,从而为正确地分析和构建实际的复杂软件系统奠定坚实的基础。
学生在完成本课程学习后,应能够:1.理解软件体系结构的相关概念;2.掌握如何将复杂的软件系统按产品特征划分为子系统,以及如何规范子系统的构成;3.掌握如何应用模式的方法构造复杂软件的解决方案;4.掌握一些常见设计模式的应用环境及解决的问题,并能在实践中根据需要应用这些模式。
课程简介:《软件体系结构》主要是为软件工程/计算机专业的高年级本科学生,特别是软件工程方向的学生所开设的课程。
本课程系统地介绍软件体系结构的基本原理、方法和实践,介绍软件体系结构的设计和应用实例,强调理论与实践相结合。
本课程重点讲解基于模式的软件体系结构描述方法、软件体系结构风格和设计模式、基于产品特征的软件开发/重构方法、设计模式作为解决方案空间的有效工具等内容。
软件体系结构的模式描述、产品特征表达与模式设计和重构、在实践中如何应用产品特征来划分和规范子系统等内容是本课程的难点。
本课程采用课堂讲解与课程实验相结合的方法,辅以一定的案例讲解,帮助学生加强理解,更好地掌握课程内容。
教学内容:本课程内容共分6部分:1.软件体系结构概念主要讲授软件体系结构的发展历程和基本概念、软件体系结构设计的基本原理、研究软件体系结构的意义、当前研究状况等内容。
1.1 设计正在“腐烂”的征兆(Symptoms of Rotting Design)有四个主要的征兆告诉我们该软件设计正在“腐烂”中。
它们并不是互相独立的,而是互相关联,它们是过于僵硬、过于脆弱、不可重用性和粘滞性过高。
1. 过于僵硬Rigidity Rigidity 致使软件难以更改,每一个改动都会造成一连串的互相依靠的模块的改动,项目经理不敢改动,因为他永远也不知道一个改动何时才能完成。
2. 过于脆弱Fragility Fragility 致使当软件改动时,系统会在许多地方出错。
并且错误经常会发生在概念上与改动的地方没有联系的模块中。
这样的软件无法维护,每一次维护都使软件变得更加难以维护。
(恶性循环)3. 不可重用性immobility immobility 致使我们不能重用在其它项目中、或本项目中其它位置中的软件。
工程师发现将他想重用的部分分离出来的工作量和风险太大,足以抵消他重用的积极性,因此软件用重写代替了重用。
4. 粘滞性过高viscosity viscosity有两种形式:设计的viscosity和环境的viscosity.当需要进行改动时,工程师通常发现有不止一个方法可以达到目的。
但是这些方法中,一些会保留原有的设计不变,而另外一些则不会(也就是说,这些人是hacks)。
一个设计如果使工程师作错比作对容易得多,那么这个设计的viscosity 就会很高。
环境的viscosity高是指开发环境速度很慢且效率很低。
2 面向对象的类设计原则2.1 开放关闭原则The Open Closed Principle (OCP)A module should be open for extension but closed for modification.一个模块应该只在扩展的时候被打开(暴露模块内部),在修改的时候是关闭的(模块是黑盒子)。
在所有的面向对象设计原则中,这一条最重要。
该原则是说:我们应该能够不用修改模块的源代码,就能更改模块的行为。
种设计模式及事例重点时辰,第一时间送到!个人Github-24 种设计模式事例链接种设计模式案例维导图创立型模式工厂模式工厂模式(FactoryPattern)是Java中最常用的设计模式之一。
这类种类的设计模式属于创立型模式,它供应了一种创立对象的最正确方式。
在工厂模式中,我们在创立对象时不会对客户端裸露创立逻辑,并且是经过使用一个共同的接口来指向新创立的对象。
介绍企图:定义一个创立对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创立过程延缓到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不一样条件下创立不一样实例时。
怎样解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
重点代码:创立过程在其子类履行。
应用实例:1、您需要一辆汽车,能够直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的详细实现。
2、Hibernate换数据库只需换方言和驱动就能够。
长处:1、一个调用者想创立一个对象,只需知道其名称就能够了。
2、扩展性高,假如想增添一个产品,只需扩展一个工厂类就能够。
3、障蔽产品的详细实现,调用者只关怀产品的接口。
弊端:每次增添一个产品时,都需要增添一个详细类和对象实现工厂,使得系统中类的个数成倍增添,在必定程度上增添了系统的复杂度,同时也增添了系统详细类的依靠。
这其实不是什么好事。
使用处景:1、日记记录器:记录可能记录到当地硬盘、系统事件、远程服务器等,用户能够选择记录日记到什么地方。
2、数据库接见,当用户不知道最后系统采纳哪一类数据库,以及数据库可能有变化时。
3、设计一个连结服务器的框架,需要三个协议,'POP3'、'IMAP'、'HTTP',能够把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创立类模式,在任何需要生成复杂对象的地方,都能够使用工厂方法模式。
有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要经过new 就能够达成创立的对象,无需使用工厂模式。
设计模式(OOD)学习设计模式(Object-Oriented Design Patterns)是在软件开发中用来解决经常出现的问题的一种经验总结和方法论。
设计模式通过提供一套经过验证的解决方案来帮助开发人员更高效地设计和组织代码。
本文将介绍设计模式的概念、常见的设计模式分类以及一些常用的设计模式示例。
设计模式的概念设计模式是一种软件设计中的抽象,它描述了解决常见问题的一种方法。
它们不是一种具体的算法或代码实现,而是一种在特定情况下可重复使用的解决方案。
设计模式的目标是提高代码的可重用性、可读性和可维护性。
常见的设计模式分类常见的设计模式可以根据其目标和应用场景进行分类。
以下是几个常见的设计模式分类:1. 创建型模式(Creational Patterns)创建型模式关注对象的实例化过程。
它们提供了一种将对象的创建和使用分离的方法,隐藏了对象的创建细节。
- 单例模式(Singleton Pattern):保证一个类只有一个实例,并提供全局访问点。
- 工厂模式(Factory Pattern):通过使用工厂方法来创建对象,而不是直接使用new操作符。
- 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。
- 原型模式(Prototype Pattern):通过克隆现有对象来创建新对象。
- 建造者模式(Builder Pattern):将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
2. 结构型模式(Structural Patterns)结构型模式关注类和对象的组合,通过定义对象之间的关系来实现更大的结构。
- 适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另一个接口。
- 装饰器模式(Decorator Pattern):动态地将新功能添加到对象中。
- 代理模式(Proxy Pattern):为其他对象提供一个代理以控制对这个对象的访问。
OOAD-设计模式(⼀)概述前⾔ 在我们很多时候设计代码都是需要⽤到各种不⼀样的设计模式的,接下来着⼏篇给⼤家领略⼀下设计模式。
知道设计模式的作⽤,以及在代码的具体体现。
很多时候我们看不懂代码就是因为我们不知道它使⽤的设计模式。
国庆的我很痛苦,学习是痛苦的,成长是快乐的!⼀、什么是⾯向对象1)⾯向对象(Object-Orientation,简称OO)是⼀种系统建模技术/编程思想。
2)⾯向对象编程(Object-Orientation Programming,简称OOP)是按照OO的⽅法学来开发程序的编程⽅式3)⾯向对象分析⽅法 Object-Oriented Analysis,简称OOA分析阶段主要解决以下问题:建⽴针对业务问题域的清晰视图列出系统必须要完成的核⼼任务针对问题域建⽴公共词汇表列出针对此问题域的最佳解决⽅案此阶段要解决的核⼼问题是"what to do?"4)⾯向对象设计Object-Oriented Design, 简称OOD设计阶段主要解决以下问题:如何解决具体的业务问题引⼊系统⼯作所需的各⽅⾯的⽀持元素定义系统的实现策略 此阶段要解决的核⼼问题是"How to do?"5)OO: ⾯向对象,是⼀套集编程思想,⽅法,原则,模式,解决⽅案等为⼀体的编程模式。
OO的思想贯穿于整个软件开发的过程,⽐如需求分析,设计,编程,测试,升级等。
综上可以知道什么是OOAD?OOAD(Object Oriented Analysis Design,的分析和设计,)是现代软件企业⼴为采⽤的⼀项有效技术。
OOAD⽅法要求在设计中要映射现实世界中指定中的对象和实体,例如:顾客、汽车和销售⼈员等。
这就需要设计要尽可能地接近现实世界,即以最⾃然的⽅式表述实体。
所以的优点即为能够构建与现实世界相对应的问题模型,并保持他们的结构、关系和⾏为为模式。
⼆、⾯向对象的特点2.1、抽象 抽象就是将⼀些事物的共性和相似点抽离出来,并将这些属性归为⼀个类,这个类只考虑这些事物的共性和相似之处,并且会忽略与当前业务和⽬标⽆关的那些⽅⾯,只将注意⼒集中在与当前⽬标有关的⽅⾯。
一些面向对象的设计法则法则1:优先使用(对象)组合,而非(类)继承[ Favor Composition Over Inheritance ]组合(对象)组合是一种通过创建一个组合了其它对象的对象,从而获得新功能的复用方法。
将功能委托给所组合的一个对象,从而获得新功能。
有些时候也称之为“聚合”(aggregation)或“包容”(containment),尽管有些作者对这些术语赋予了专门的含义例如:)聚合:一个对象拥有另一个对象或对另一个对象负责(即一个对象包含另一个对象或是另一个对象的一部分),并且聚合对象和其所有者具有相同的生命周期。
(译者注:即所谓的“同生共死”关系,可参见GOF的Design Patterns: Elements of ReusableObject-Oriented Software的引言部分。
))包容:一种特殊类型的组合,对于其它对象而言,容器中的被包含对象是不可见的,其它对象仅能通过容器对象来访问被包含对象。
(Coad)包含可以通过以下两种方式实现:)根据引用(By reference))根据值(By value)C++允许根据值或引用来实现包含。
但是在Java中,一切皆为对象的引用!组合的优点和缺点优点:)容器类仅能通过被包含对象的接口来对其进行访问。
)“黑盒”复用,因为被包含对象的内部细节对外是不可见。
)对装性好。
)实现上的相互依赖性比较小。
(译者注:被包含对象与容器对象之间的依赖关系比较少))每一个类只专注于一项任务。
)通过获取指向其它的具有相同类型的对象引用,可以在运行期间动态地定义(对象的)组合。
缺点:)从而导致系统中的对象过多。
)为了能将多个不同的对象作为组合块(composition block)来使用,必须仔细地对接口进行定义。
继承(类)继承是一种通过扩展一个已有对象的实现,从而获得新功能的复用方法。
泛化类(超类)可以显式地捕获那些公共的属性和方法。
【最新编排】---------------------------------------------------------------------------------------------------------------------- 面向对象设计原则开-闭原则里氏转换原则依赖倒转原则接口隔离原则组合/聚合复用原则迪米特法则单-职责"开--闭"原则---核心原则Software entities should be extension,but closed for modification.软件实体对扩展开发,对修改关闭.当软件系统面对着新地需求地时候,系统地设计是稳定地.满足"开-闭"原则系统地优点:1) 通过扩展已有地模块,提供新地行为,满足新需求,使得变化中地软件系统有-定地适应性和灵活性2) 已有地软件模块,特别是最重要地抽象层模块不能再修改,这就使得变化中地模块有-定地稳定性和延续性.怎样才能做到开闭原则?1) 抽象化,为系统定义-个不再更改地抽象设计,这个设计预见了所有可能地变化.满足了开闭原则地"闭"2) 找到系统中变化地部分,把可变性封装起来.使用继承或者其他方式来封装变化,将变化封装在-个方法中或者-个类中."里氏转换"原则(LSP)定义:如果-个软件实体使用-个基类地话,那么-定适合于它地子类.也就是基类出现地地方,子类-定可以出现,替换后软件行为不会发生变化, 而且它根本不能识别出基类和子类对象地区别.里氏转换原则是对开-闭原则地补充.违反了里氏原则,有就违反了开闭原则;反之不成立. 里氏转换原则是继承复用地基础.只有当衍生类可以替换掉基类,软件功能不会受到影响地时候,基类才能被真正复用,而衍生类才能在基类地基础上增加新地行为.理氏转换原则是代理模式成立地基础.代理模式和真实主题模式都是抽象主题角色地子类.客户端只知道抽象主题,而代理主题可以替代抽象主题出现在任何地方"依赖倒转"原则抽象不应该依赖于细节, 细节应该依赖于抽象.(高层模块不应该依赖于底层模块,两个都依赖于抽象)通俗地说:面向接口编程, 不要对实现编程."接口隔离"原则使用专门地接口比使用-个总地接口好;-个类对另外-个类地依赖性应当建立在最小地接口上地.接口理解成角色,-个接口就只是代表-个角色,每个角色都有它特定地-个接口,这里地这个原则可以叫做"角色隔离原则".OOD设计地时候,准确地划分角色以及角色对应地接口.将没有关系地接口合并在-起,就会对接口和角色构成污染."组合/聚合复用"原则要尽量使用合成/聚合达到复用, 尽量少用继承.将-个已经有地对象纳入新对象中,使之成为新对象地-部分, 新对象可以调用引入地旧对象地方法和功能.优势:1)新对象存取成分对象地唯-方法是通过成分对象地接口.2)这种对象地复用是黑箱复用,因为成分对象地内部实现细节对于新地对象是看不见地. 3)这种复用支持包装.4)新对象可以在运行地时候动态地引用于成分对象类型相同地对象.继承复用地优势:1)新地实现较为容易,因为超类地大部分功能可以通过继承关系自动进入子类.2) 修改或者扩展继承而来地实现比较容易.继承复用地缺点:1) 继承复用破坏包装.将超类地实现细节暴露给子类.超类地内部细节常常对子类是透明地,白箱复用.2)超类地实现发生了改变,子类地实现也不得不改变3) 超类继承而来地是静态地,不可能在运行时间内发生改变.因此没有足够地灵活性."迪米特"原则又叫最少知识原则;-个对象应当对其他对象有尽可能少地了解.只与你地朋友们通信,不要和陌生人说话;使民无知不相往来: 将被统治地对象隔离开来,使它们没有直接地通信,可以达到分化瓦解,继而分而治之地效果迪米特法则能够减少耦合.类之间地耦合越小,越有利于复用."定制服务"设计原则,只向客户端提供客户端需要地方法,其他地不需要地不提供,这也是符合迪米特法则地.单-职责原则就-个类而言,应该仅有-个引起他变化地原因.如果-个类承担地职责过多,就等于把这些职责耦合在-起,-个职责地变化可能会削弱或者抑制这个类完成其他职责地能力.这种耦合会导致脆弱地设计.当变化发生地时候,设计会遭受意想不到地破坏.对设计而言,就是发现职责并把这些职责分离.。
游戏开发中常⽤的设计模式 使⽤设计模式来提⾼程序库的重复利⽤性是⼤型程序项⽬开发必须的。
但是在“四⼈帮”的设计模式概述中提到了23种标准设计模式,不但难以记住,⽽且有些设计模式更多的适⽤于应⽤程序开发,对游戏项⽬引擎设计并没有很多的利⽤价值。
根据经验,精挑细选后,笃志在这⾥记录⼀些⾃认为有利⽤价值的设计模式,以便之后⾃⼰设计时使⽤。
⼀:观察者Observer观察者的设计意图和作⽤是:它将对象与对象之间创建⼀种依赖关系,当其中⼀个对象发⽣变化时,它会将这个变化通知给与其创建关系的对象中,实现⾃动化的通知更新。
游戏中观察者的适⽤环境有:1:UI控件管理类。
当我们的GUI控件都使⽤观察者模式后,那么⽤户的任何界⾯相关操作和改变都将会通知其关联对象-----我们的UI事件机。
2:动画管理器。
很多时候我们在播放⼀个动画桢的时候,对其Frame有很⼤兴趣,此时我们设置⼀个FrameLister对象对其进⾏监视,获得我们关⼼的事件进⾏处理是必须的。
观察者伪代码:// 被观察对象⽬标类Class Subject{// 对本⽬标绑定⼀个观察者 Attach( Observer );// 解除⼀个观察者的绑定 DeleteAttach( Observer );// 本⽬标发⽣改变了,通知所有的观察者,但没有传递改动了什么Notity(){For ( …遍历整个ObserverList …){ pObserver ->Update(); }}// 对观察者暴露的接⼝,让观察者可获得本类有什么变动GetState();}//-------------------------------------------------------------------------------------------------------// 观察者/监听者类Class Observer{// 暴露给对象⽬标类的函数,当监听的对象发⽣了变动,则它会调⽤本函数通知观察者Void Update (){pSubject ->GetState(); // 获取监听对象发⽣了什么变化TODO:DisposeFun(); // 根据状态不同,给予不同的处理}}⾮程序语⾔描述:A是B的好朋友,对B的⾏为⾮常关⼼。