软件设计模式刘淼gzliumiao@yahoo.com.cn电话:668328设计模式-可复用面向对象软件的基础•历史性著作《设计模式:可复用面向对象软件的基础》一书中描述了23种经典面向对象设计模式,创立了模式在软件设计中的地位。•由于《设计模式》一书确定了设计模式的地位,通常所说的设计模式隐含地表示“面向对象设计模式”。但这并不意味“设计模式”就等于“面向对象设计模式”。课程介绍•课程的地位–计算机软件工程专业模块课程。•教学内容–GoF模式:23种•教学目标–理解和运用•学时:36•成绩评定:–平时:50%(考勤、提问、课堂表现和讨论课发言)–大作业:50%第1章引言•第1句话–设计面向对象软件比较困难,而设计可复用的面向对象软件就更加困难。•软件工程的追求目标之一:复用性–源代码复用——继承–二进制代码复用——SDK、组件技术–框架软件复用•Struts2、Spring、Hibernate–设计经验的复用—设计模式1.1什么是设计模式•定义:–特定环境中问题的成功解决方案中的静态、动态结构,以及结构元素相互之间的协作关系•Designpatternsrepresentsolutionstoproblemsthatarisewhendevelopingsoftwarewithinaparticularcontext–每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。•Adescriptionofarecurrentproblemandofthecoreofpossiblesolutions.–Asolutiontoaproblemthatoccursrepeatedlyinavarietyofcontexts.1.1什么是设计模式•模式的4个要素–模式名称•一个助记名,望文生义,用一两个词来描述模式的问题、解决方案和效果。–问题•描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,如怎样用对象表示算法等。也可能描述了导致不灵活设计的类或对象结构。1.1什么是设计模式•模式的4个要素–解决方案•描述了设计模式的组成成分,它们之间的相互关系及各自的职责和协作方式。–效果•模式应用的效果及使用模式应权衡的问题。1.2MVC中的设计模式•MVC–一种软件体系结构–近年来广泛应用于各种Web框架中1.2MVC中的设计模式•模型(Model)–应用对象•视图(View)–应用对象在屏幕上的显示对象–可以为一个模型提供不同的视图对象•控制器(Controller)–对用户输入的响应方式–可以为一个视图提供不同的响应对象1MVC中的Observer模式A=10%B=40%C=30%D=20%ApplicationdataABCDADCBRelativePercentagesY10403020X15353515Z10403020ABCDChangenotificationRequests,modifications2MVC中的Composite模式•嵌套视图——组合视图–一个组合视图可用于任何视图可用的地方,但是它包含并管理嵌套于其中的视图3MVC中的Strategy模式•View-Controller之间的关系–将响应机制封装在Controller中,存在一个Controller类层次结构。–View使用Controller子类的实例来实现一个特定的响应策略。要实现不同响应策略,只要用不同种类的Controller实例替换即可。1.3描述设计模式•固定模板(13部分)–1模式名和分类•AbstractFactory(抽象工厂)——对象创建型模式–2意图•解决什么问题,目的是什么?–3别名–4动机•问题说明及如何解决该问题1.3描述设计模式•固定模板(13部分)–5适用性•适用情况–6结构•使用类图来描述模式中的类或对象之间的联系–7参与者•模式中的类或对象及其各自职责–8协作•参与者如何协作以实现各自的职责1.3描述设计模式•固定模板(13部分)–9效果•使用模式后的优点–10实现•实现时应注意的事项–11代码示例•用C++或SmallTalk实现该模式的代码片断–12已知应用•实际系统中发现的该模式的例子–13相关模式•与该模式紧密相关的其它模式?如何配合使用?1.4设计模式的编目•23种模式1.5设计模式的分类•按目的分类–创建型:与对象的产生相关–结构型:处理类或对象的组合–行为型:对象之间如何交互,怎样分配职责。•按范围分类–类模式:处理类之间的关系–对象模式:处理对象之间的联系1.5设计模式的分类•结合上述两种标准,将设计模式划分为6类:–创建型类模式•将对象的创建工作延迟到子类–创建型对象模式•将对象的创建工作延迟到另一个对象中–结构型类模式•使用继承机制组合类–结构型对象模式•描述了对象的组装方式–行为型类模式•使用继承描述算法或控制流–行为型对象模式•对象之间如何协作来完成单个对象无法完成的任务设计模式之间的关系1.6设计模式如何解决设计问题•1.6.1寻找合适的对象–OOA•通过用例描述寻找领域对象–OOD•通过用例设计发现抽象对象•所谓抽象,是指与领域对象比较而言,如算法类。1.6设计模式如何解决设计问题•1.6.2决定对象的粒度–许多模式涉及到对象的分解问题。1.6设计模式如何解决设计问题•1.6.3指定对象的接口–类型是用来标示特定接口的一个名字–对象是封闭的,只有通过接口才能与外界交流。–动态绑定–设计模式通过确定接口的主要组成部分来帮助定义接口–设计模式也指定了接口之间的关系1.6设计模式如何解决设计问题•1.6.4描述对象的实现–类继承与接口继承的比较•类(class)与类型(type)–类定义了对象的实现——具体类–类型用来标示接口——接口或抽象类–针对接口编程,而不是针对实现编程•客户类不用知道他们使用的对象是何种具体类,只需知道是何种类型即可,降低了相互间的耦合度。–不将变量声明为某个具体类的实例对象,而是让他遵从抽象类定义的接口,是设计模式中的常用办法。1.6设计模式如何解决设计问题•1.6.5运用复用机制–继承和组合的比较•继承——白箱复用•组合——黑箱复用–允许你在运行时刻改变被组合的行为•优先使用对象组合,而不是继承。–委托(代理)•对象组合的特例–继承和参数化类型的比较•模板类:允许在定义一个类型时并不指定该类型所用到的其它类型。–允许你改变类所用到的类型1.6设计模式如何解决设计问题•1.6.6运行时刻和编译时刻的结构–运行时刻和编译时刻的结构•差别很大–编译时刻:继承关系固定的类组成–运行时刻:由快速变化的对象网络组成–聚合和关联的比较•表示区别•实现方法相同1.6设计模式如何解决设计问题•1.6.7设计应支持变化–导致重新设计的常见原因•显式地创建一个对象;•对特殊操作的依赖;•对硬件和软件平台的依赖;•对对象表示或实现的依赖;•算法依赖;•紧耦合•通过生成子类来扩充功能;•不能方便地对类进行修改。1.6设计模式如何解决设计问题•1.6.7设计应支持变化–设计模式在开发3类主要应用中的作用•应用程序–优先考虑内部复用性、可维护性和可扩充性–设计模式通过减少依赖性来提高内部复用性。–通过显示怎样扩展类层次结构和使用对象复合,增强可扩充性。1.6设计模式如何解决设计问题•1.6.7设计应支持变化–设计模式在开发3类主要应用中的作用•工具箱–是一组相关的、可复用的类的集合,提供通用的功能。–避免假设和依赖。1.6设计模式如何解决设计问题•1.6.7设计应支持变化–设计模式在开发3类主要应用中的作用•框架–规定了应用的体系结构。–松散耦合更重要。1.6设计模式如何解决设计问题•1.6.7设计应支持变化–设计模式和框架的区别•设计模式比框架更抽象•设计模式是比框架更小的体系结构元素•框架比设计模式更加特例化1.7怎样选择设计模式•考虑设计模式是怎样解决设计问题的•阅读模式的意图部分•研究模式如何关联•研究目的相似的模式•检查重新设计的原因•考虑你的设计中哪些是可变的–表1-21.8怎样使用设计模式•理解记忆•在软件开发中使用设计模式的使用限制•通过引入额外的间接层次获得灵活性和可变性的同时,使设计更复杂,降低运行性能。•只有当模式提供的灵活性是真正需要时,才有必要使用。面向对象设计的五个原则(补充)•设计模式综合运用了这些原则面向对象设计的五个原则(补充)•1开放封闭原则(OCP)–软件对扩展应该是开放的,对修改应该是关闭的。更通俗的表达就是说开发一个软件时,应该可以对它进行功能扩展(开放),而在进行这些扩展的时候,不需要对原来的程序进行修改(关闭)。–好处:在软件可用性上非常灵活。可以在软件完成对软件进行扩展,加入新的功能。这样,这个软件就可以通过不断的增加新模块满足不断变化的新需求。由于对软件原来的模块不能修改,因此不用担心软件的稳定性。面向对象设计的五个原则(补充)•1开放封闭原则(OCP)–实现的主要原则:抽象,把系统的所有可能的行为抽象成一个抽象顶层;同时由于可以从抽象层导出一个或多个新的具体类可改变系统的行为,因此对于可变的部分,系统设计对扩展是开放的。面向对象设计的五个原则(补充)•1开放封闭原则(OCP)面向对象设计的五个原则(补充)•1开放封闭原则(OCP)–在设计开始阶段,对所有的可变因素进行预计和封装也不太现实,也是很难做得到。所以,开闭原则描绘的愿景只是一种理想情况或是极端状态,现实世界中是很难被完全实现的。我们只能在某些组件,在某种程度上符合开闭原则的要求。面向对象设计的五个原则(补充)•2单一职责原则(SRP)–结构化设计•降低模块间的耦合,增加模块内聚度•上述原则在OOD中的延伸就是单一职责原则面向对象设计的五个原则(补充)•2单一职责原则(SRP)–单一职责,通常意味着单一的功能,因此不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。•功能多了,任何一个功能发生变化,就要修改该类,这种修改可能会对其他功能产生影响。•违反了低耦合、高内聚原则Farhana:如何违反的?Shubho:你看,Rectangle类做了两件事。在一个方法里它计算了面积,在另外一个方法了它返回一个表示矩形的GUI。这会带来一些有趣的问题:在计算几何应用程序中我们必须包含GUI。也就是在开发几何应用时,我们必须引用GUI库;图形应用中Rectangle类的变化可能导致计算几何应用变化,编译和测试,反之亦然;Farhana:有点意思。那么我猜我们应该依据职责拆分这个类,对吗?Shubho:非常对,你猜我们应该做些什么?Farhana:当然,我试试。下面是我们可能要做的:拆分职责到两个不同的类中,如:Rectangle:这个类应该定义Area()方法;RectangleUI:这个类应继承Rectangle类,并定义Draw()方法。Shubho:非常好。在这里,Rectangle类被计算几何应用使用,而RectangleUI被图形应用使用。我们甚至可以分离这些类到两个独立的DLL中,那会允许我们在变化时不需要关心另一个就可以实现它。Farhana:谢谢,我想我明白SRP了。SRP看起来是把事物分离成分子部分,以便于能被复用和集中管理。我们也不能把SRP用到方法级别吗?我的意思是,我们可以写一些方法,它们包含做很多事的代码。这些方法可能违反SRP,对吗?Shubho:你理解了。你应当分解你的方法,让每个方法只做某一项工作。那样允许你复用方法,并且一旦出现变化,你能够以修改最少的代码满足变化。这里,Rectangle类做了下面两件事:计算矩形面积;在界面上绘制矩形;并且,有两个应用使用了Rectangle类:计算几何应用程序用这个类计算面积;图形程序用这个类在界面上绘制矩形;这违反了SRP(单一职责原则);面向对象设计的五个原则(补充)•3LisKov替换原则(LSP)–定义:“继承必须确保超类所拥有的性质在子类中仍然成立。”也就是说,当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is