设计模式01郑州大学软件学院赵哲主要内容•为什么要学习设计模式•设计模式准备:7大设计原则郑州大学软件学院赵哲三个例子Image{abstract}BMPImageBMPWindowsImpJPGImageGIFImagePNGImageBMPLinuxImpBMPUnixImpJPGWindowsImpJPGLinuxImpJPGUnixImpGIFWindowsImpGIFLinuxImpGIFUnixImpPNGWindowsImpPNGLinuxImpPNGUnixImp三个例子//电影票类classMovieTicket{privatedoubleprice;//电影票价格privatestringtype;//电影票类型……//计算打折之后的票价publicdoubleCalculate(){//学生票折后票价计算if(this.type.Equals(student)){Console.WriteLine(学生票:);returnthis.price*0.8;}//儿童票折后票价计算elseif(this.type.Equals(children)&&this.price=20){Console.WriteLine(儿童票:);returnthis.price-10;}//VIP票折后票价计算elseif(this.type.Equals(vip)){Console.WriteLine(VIP票:);Console.WriteLine(增加积分!);returnthis.price*0.5;}else{returnthis.price;//如果不满足任何打折要求,则返回原始票价}}}三个例子银行业务处理系统第三方算法库DataOperation客户端类加密类如何兼容?设计模式解决问题•类过于庞大•类在设计时实现多复用、高内聚、低耦合•容易兼容新接口•等七大设计原则•单一职责原则•开-闭原则•里氏代换原则•依赖倒转原则•接口隔离原则•合成/聚合复用原则•迪米特原则郑州大学软件学院赵哲单一职责原则•一个类只有一种职责•SRPSingleResponsibilityPrinciple[ˈprinsəpl]•软件设计真正要做的许多内容,就是发现职责并且把职责相互分离•为什么?•高内聚、低耦合、多复用郑州大学软件学院赵哲一个例子郑州大学软件学院赵哲GameLogin()Exit()Left()Right()Fire()getUserName()getHighScore()思考问题•这个类中有几个职责?•用户是否登陆的方法跟用户玩游戏的方法是否有关?•获得用户信息跟其他两个职责是否有关?•如何优化代码?郑州大学软件学院赵哲优化完成UML类图郑州大学软件学院赵哲userStateLogin()Exit()PlayLeft()Right()Fire()userInforgetUserName()getHighScore()内聚高还是低?耦合度呢?开-闭原则•对扩展开放、对更改封闭•OpenforextensionClosedformodification•开放-封闭原则是面向对象设计的核心所在•做一个例子:•写个代码片段:•完成对两个数字的加分和减法郑州大学软件学院赵哲开-闭原则•大部分人的做法:郑州大学软件学院赵哲若要添加乘法和除法呢?开-闭原则郑州大学软件学院赵哲OperaterGetResult(a,b)减GetResult(a,b)加GetResult(a,b)符合开闭原则回顾•SRPSingleResponsibilityPrincipleGameLogin()Exit()Left()Right()Fire()getUserName()getHighScore()优化完成UML类图郑州大学软件学院赵哲userStateLogin()Exit()PlayLeft()Right()Fire()userInforgetUserName()getHighScore()开闭原则•OpenforextensionClosedformodificationOperaterGetResult(a,b)减GetResult(a,b)加GetResult(a,b)几个概念•封装、继承、多态•抽象类•接口•静态类•=================================•重载overload•重写(覆盖)override里氏代换原则•BarbaraLiskow在1988年发表的•1972年Barbara成为麻省理工学院的教授•是美国获得计算机科学博士学位的第一人郑州大学软件学院赵哲19里氏代换原则•LiskowSubstitutionPrinciple(LSP):子类必须能够替换掉它们的父类型•只要是基类出现的地方,一定能够出现子类!•满足里氏代换原则才可以继承郑州大学软件学院赵哲20LSP•Animalanimal=newAnimal();•xiaoMing.love(animal);•Animalcat=newCat();•xiaoMing.love(cat);LSP•例子•孙悟空勾掉生死簿中所有猴子的名字,那么猕猴、石猴、长臂猿等的名字是否都勾掉了?•注意LSP反之不成立!•《墨子小取》娣,美人也,爱娣,非爱美人也….盗,人也;恶盗,非恶人也郑州大学软件学院赵哲22LSP•例子:•一个例子:长方形和正方形•某软件while(长=宽),将宽+1.•父类没问题,但子类就回出现死循环•此种情况下正方形不可以继承长方形郑州大学软件学院赵哲23一个例子郑州大学软件学院赵哲依赖•虚线普通箭头表示依赖•依赖和关联的区别•A和B关联,A只认识B,知道B的属性和方法,可以双关联,但不建议使用。•A依赖B•B的改变影响A的结果。•通常依赖是单向的郑州大学软件学院赵哲郑州大学软件学院赵哲×如果父类的某些方法在子类中已经发生畸变,则建议断开父子关系!郑州大学软件学院赵哲总结LSP•拒绝子类有自己的个性•一旦子类有了“个性”,则与父类之间的关系难以调和•会让代码的耦合变得扑朔迷离•只要是基类出现的地方,一定能够出现子类!•满足里氏代换才允许继承郑州大学软件学院赵哲依赖倒转原则•要针对接口编程,不要针对实现编程。•Programtoaninterface,notanimplementation.•高层模块不应该依赖底层模块,两者应该都依赖抽象•High-levelmoduleshouldnotdependuponbottommodule.bothshoulddependuponabstractions郑州大学软件学院赵哲29依赖倒转举例•开关和电灯•开关不应该依赖电灯,电灯也不应该依赖开关•两者都依赖抽象,也就是各自的接口(协议)•为什么?依赖倒转原则•减少类之间的耦合度•降低风险•一个例子•司机开车郑州大学软件学院赵哲Driverdriver(Benzbz)BenzRun()依赖倒转原则•司机还要开宝马,如何实现?•增加宝马类,但是需要修改司机类郑州大学软件学院赵哲BMWRun()Driverdriver(Benzbz)driver(BMWbmw)BenzRun()×如何解决•针对接口编程郑州大学软件学院赵哲实现场景代码:•张三开奔驰•IDriver张3=newDriver();•ICarbenz=newBenz();•张3.driver(benz);郑州大学软件学院赵哲练习•最初:光明农场养牛•扩展:多种牛场、喂养多种动物•最初类图如下,按照依赖倒转原则画出正确类图郑州大学软件学院赵哲光明农场Feed(Cowc)CowEat()接口隔离原则•InterfaceSegregationPrinciple•ISP•客户端不应该依赖它不需要的方法•类间的依赖关系应该建立在最小的接口上郑州大学软件学院赵哲接口隔离•用电脑做什么?•写作,通讯,看电影,打游戏,上网,编程,计算,数据服务等•上网本、PC机、服务器实现不同接口•所以,我们定义接口:工作学习接口,编程开发接口,上网娱乐接口,计算和数据服务接口,这样,我们的不同功能的电脑就可以有所选择地继承这些接口。一个例子•一个接口:内容是雇佣者有吃饭和做工作的方法•雇佣的男、女都可以使用该接口•有一天,雇佣者来了一个机器人。•机器人不吃饭郑州大学软件学院赵哲例子-错误InterfaceIWorker{voideat();voidwork();}ClassWorker:IWorker{publicvoideat(){//eat相关代码}publicvoidwork(){//work相关代码}}郑州大学软件学院赵哲39ISP•例子的缺陷•胖接口,造成了对接口的污染•如何解决?•客户端不应该依赖它不需要的方法•类间的依赖关系应该建立在最小的接口上•一个接口只做一件事郑州大学软件学院赵哲40例子-符合ISPInterfaceIWorkerEat{voideat();}InterfaceIWorkerWork{voidwork();}ClassWorker:IWorkerEat,IWorkerWork{publicvoideat(){//eat相关代码}publicvoidwork(){//work相关代码}}ClassRobot:IWorkerWork{publicvoidwork(){//work相关代码}}郑州大学软件学院赵哲41面向接口编程,而非实现编程ISP•胖接口容易导致哑方法•瘦接口更健康!郑州大学软件学院赵哲42合成/聚合复用原则•Composite/AggregateReusePrinciple•CARP•合成和聚合•合成是强烈的关联,部分(将)和整体(主)生命周期一样,整体(主)对部分(将)有支配权,包括创建和销毁。•聚合是部分和整体的普通关联郑州大学软件学院赵哲合成和聚合•刘邦和韩信郑州大学软件学院赵哲44CARP原则合成:classPerson{privateHandhand;publicPerson(){hand=newHand();}}郑州大学软件学院赵哲45CARP原则聚合:classPerson{privateComputercomputer;privateAa;publicsetComputer(){computer=newComputer();}publicsetA(){a=newa();}}郑州大学软件学院赵哲46CARP原则•尽量使用合成/聚合,而不是使用继承•继承的优点–新的实现较为容易,因为基类的大部分功能可以通过继承的关系自动进入子类。–修改和扩展继承而来的实现较为容易。•继承的缺点–继承将基类的实现细节暴露给子类。称“白箱”复用。–如果基类发生改变,那么子类的实现也不得不发生改变。–多继承不利于维护郑州大学软件学院赵哲47CARP特点•优点:–依赖少,条条框框的限制少–几乎可以用到任何环境中–容易实现–修改和扩展容易•缺点:–对象多,需要管理郑州大学软件学院赵哲48迪米特原则•TheLawofDemeter•LoD•不要和陌生人说话•又叫最少知识原则•小国寡民,邻国相望,鸡犬之声相闻,民至老死,不相往来郑州大学软件学院赵哲迪米特法则•一个对象应当对其他对象有尽可能少的了解,不和陌生人说话•最少知识、不相往来•talkonlytoyourimmediatefriends郑州大学软件学院赵哲50Form1Form2Form3Form4Form5DAO1DAO2DAO3DAO4Controller1Controller2迪米特法则的优点•相当弱的类耦合•利于复用•只要有可能类就可以设计成不变类郑州大学软件学院赵哲53迪米特法则的缺点•造成大量的小方法,散落在系统的各个角落•这些方法仅仅是传递间接的调用•容易引起第一次了解系统人的困惑郑州大学软件学院赵哲54迪米特法则和设计模式•Façade模式和Mediator模式都符合迪米特法则郑州大学软件学院赵哲55总结•泛化、依赖、关联、聚合、组合•泛化(继承)•classA{}•classB:A{}•依赖:•class