《软件设计原则》PPT课件.ppt

上传人:牧羊曲112 文档编号:4861256 上传时间:2023-05-20 格式:PPT 页数:111 大小:3.74MB
返回 下载 相关 举报
《软件设计原则》PPT课件.ppt_第1页
第1页 / 共111页
《软件设计原则》PPT课件.ppt_第2页
第2页 / 共111页
《软件设计原则》PPT课件.ppt_第3页
第3页 / 共111页
《软件设计原则》PPT课件.ppt_第4页
第4页 / 共111页
《软件设计原则》PPT课件.ppt_第5页
第5页 / 共111页
点击查看更多>>
资源描述

《《软件设计原则》PPT课件.ppt》由会员分享,可在线阅读,更多相关《《软件设计原则》PPT课件.ppt(111页珍藏版)》请在三一办公上搜索。

1、软件设计原则,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,软件可维护性设计,一个维护中的软件是不断再生的软件。修改需求,流程、界面等细节变更,修改积累,往往导致原有软件结构混乱,软件腐烂(可维护性差,可升级性差)。系统设计师:辩解用户需求变化多;软件腐烂:过于僵硬rigidity过于脆弱fragility复用率低immobility黏度高viscosity,软件可维护性设计,软件腐烂:过于僵硬rigidity添加一个新功能,很困难,牵扯多,周期长过于脆弱fragility一个地方修改,导致其他地方出现故障,甚至不可

2、预期;复用率低immobility1、想复用A模块,但A牵扯BCDE,牵扯太多,不敢被复用;2、不同模块中的重复功能,重复实现,发现问题,重复修改。黏度高viscosity1、修改,保持原有设计;2、修改破坏原有设计如果总体设计,总是导致2的实现比1容易,就是黏度过高黏度高过会诱导程序员采用错误的方案,并导致软件逐渐腐烂,软件可维护性设计,可维护性设计目标可扩展性extensibility;加入新模块,不影响原有模块灵活性flexibility;修改一个模块,不影响其他模块可插入性pluggability可以很容易的去掉一个模块,更换一个模块,加入一个新模块,而不影响其他模块。,面向对象的设计

3、原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,软件可复用性(reuse)设计1,复用的重要性:1、较高的效率;2、较高的软件质量;3、较好的可维护性。传统的复用1、复制、剪切代码;2、算法的复用;3、数据结构的复用;传统复用,往往会破坏软件的可维护性。AB共用C,如果A要修改C,B不允许。,软件可复用性(reuse)设计2,面向对象设计的复用:数据的抽象化、封装、继承、多态。复用的焦点集中在含有宏观商业逻辑的抽象层面;而不局限于函数和算法。对可维护性的支持可扩展性extensibility;封装,继承,多态由开闭原则、里氏代换原则

4、、依赖倒转原则、组合/聚合复用保证灵活性flexibility;复用,相对独立,与其他模块松耦合。该模块修改后,不会传递到其他模块。由开闭原则、迪米特法则、接口隔离原则保证。可插入性pluggability复用后,新类替代旧类,容易实现。由开闭原则、里氏代换原则、组合/聚合服用原则和依赖倒转原则保证。,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,开-闭原则1,开-闭原则OCP:open-closed principle):一个软件实体,应当对扩展开放,对修改关闭。设计一个模块时,应当使该模块在不被修改前提下被扩展;

5、即不修改源代码,而改变该模块的行为。加个代码例子满足开-闭原则的设计的优越性:1、具备适应性和灵活性;2、稳定性和延续性;实现该原则,是在更高层次上,实现了复用的、易于维护的系统。,开-闭原则2,关键点:抽象化抽象设计:给系统定义出一个一劳永逸、不再修改的抽象设计。允许此设计有无穷无尽的行为在实现层被实现。JAVA中,抽象JAVA类或JAVA接口,规定所有具体类必须提供的方法的特征,作为系统设计的抽象层。该抽象层预见了所有的可能的扩展。因此,在扩展中,抽象层不需要修改。满足了开闭原则中“对修改关闭”的原则。同时,从抽象层导出的具体新类可以改变系统的行为,又是对扩展开放的。,开-闭原则3,关键点

6、:抽象化但往往归纳较为困难汽车的定义:按照国家最新标准GB/T 3730.12001对汽车的定义:由动力驱动,具有四个或四个以上车轮的非轨道承载的车辆,主要用于:载运人员和(或)货物;牵引载运人员和(或)货物的车辆;特殊用途。本术语还包括:a)与电力线相联的车辆,如无轨电车;b)整车整备质量超过400kg的三轮车辆。电车算汽车,马拉四轮车呢?美国汽车工程师学会标准SAEJ 687C中对汽车的定义是:由本身动力驱动,装有驾驶装置,能在固定轨道以外的道路或地域上运送客货或牵引车辆的车辆。2轮摩托车算汽车吗?日本工业标准JISK 0101 中对汽车的定义是:自身装有发动机和操纵装置,不依靠固定轨道和

7、架线能在陆上行驶的车辆。,开-闭原则3,对可变性的封装原则考虑设计中什么可能会发生变化,将其封装起来,考虑允许什么发生而不让这一变化导致重新设计。意味着:1、一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里面。同一种可变性的不同表象,意味着同一个继承等级结构中的具体子类。继承,应被看做是封装变化的方法。2、一种可变性不应当与另一种可变性混合在一起。类图的继承结构,一般不会超过两层,否则,意味着两种不同的可变性混在了一起?,开-闭原则4,策略模式与开闭原则策略模式:如果有一组算法,就将每一个算法封装起来,使之可以互换?从对可变性的封装原则出发。参考策略模式?开闭原则与java日历

8、设计?如何解决。,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,JAVA语言的接口,接口:电源插座,微波炉可以,笔记本电源也可以。如果可以动态的将一个构件移走,并以另一个构件取而代之,那么这种构件就是可插入构件。插座,即接口。电器,即可插入构件可插入构件的关键在于,存在一个公用的接口。接口是实现构件的可插入性(pluggability)的关键接口的定义:接口(interface)是一些方法特征的集合,这些方法特征来自于一些在系统中不断出现的方法。,JAVA语言的接口,接口的定义:接口(interface)是一些方法特

9、征的集合,这些方法特征来自于一些在系统中不断出现的方法。接口,只有方法的特征,而没有方法的实现,因此这些方法在不同的地方被实现时,可以具有完全不同的行为。接口,与类的最重要区别:接口仅仅描述方法的特征,而不给出方法的实现。类不仅给出方法的特征,而且给出方法的实现。接口把方法的特征和方法实现分割开来。接口,类似角色,包装与该角色相关的操作和属性,而实现这个接口的类便是扮演这个角色的演员。一个角色可以由不同的演员来演;而不同的演员之间,除了可以扮演一个共同的角色之外,并不要求有其他任何共同之处。,JAVA语言的接口,接口的意义如果没有接口:对象是相互关联的,常常需要借助其他对象的行为,以便完成一项

10、工作。如果这种对其他对象行为的调用,是通过硬代码的形式写在类中,则可插入性几乎为零。类的继承,抽象超类,通过声明子类的行为,具体类继承超类,并以不同的方法实现了超类所声明的行为。客户端可以动态的决定使用哪一个具体的子类,具有一定的可插入性。但是,Java是单继承语言,一个类只能继承一个超类。对于子类,不可能加入一个新超类的功能,除非设置超超类,就是子类他爷爷类。但如此,子类具体功能的改变,往往导致结构上大范围的调整。,JAVA语言的接口,接口是对可插入性的保证关联:一个对象需要完成一项任务,需要知道并调用其他对象的方法,对象与其他对象的联系叫做关联。关联的可插入性:关联,调用类中的某一方法。I

11、nteger a=String.ToInteger(“12”)如果一个关联不是针对一个具体类,而是针对一个接口。那么任何实现该接口的类都可以满足要求。如此,就可以动态的将此关联从一个具体类转换到另一个具体类调用的可插入性调用:void mothed1(String a)一个对象不可避免的需要调用其他对象的方法。这种调用,如果是一个接口,则任何实现了该接口的具体类都可以被调用。而当前对象到底调用哪一个具体类的实例,则完全可以动态决定。,JAVA语言的接口,JAVA的类型控制JAVA接口和抽象类,用来声明一个新的类型:JAVA设计师,应当主要使用JAVA接口和抽象类将软件单位与内部和外部耦合起来,

12、实现松耦合。即:应当使用JAVA接口和抽象类,而不是具体类进行变量的类型声明、参量的类型声明、方法的返回类型声明,以及数据类型转换等。更好的做法,仅使用接口,甚至不使用抽象类。理想情况下:一个具体的java类,应当仅实现JAVA接口和抽象类中声明过的方法,而不应该给出多余的方法。,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,抽象类1,JAVA的两种类:抽象类:不可实例化,不能生成对象,但可以实现具体的方法。一定是用来继承的具体类:可以实例化,具体类,不是用来继承的。抽象类与子类的关系,实际是模板方法的应用。在抽象类

13、与具体类的树形结构中,树枝节点是抽象类,树叶节点是具体类。代码重构建议(如果B是A的子类,建立C,抽象类或接口),抽象类2,抽象类,应当拥有尽可能多的共同代码在一个从抽象类到多个具体类的继承关系中,共同的代码应当尽量向上移动到位于金字塔顶端抽象类中。这样,可以提供代码的复用率,由于代码在公共的超类,而不是子类中出现,在代码发生改变时,程序员只需修改一个地方,程序可维护性更好。抽象类,应当包含尽可能少的数据数据,会占用内存,而且复用率不高,所以尽量将数据向下移动,即尽量不要置于超类中,而是要置于具体类中。,抽象类3,针对抽象编程的核心思想针对抽象的编码,主要针对超类,而不是具体类的编程。重点,解

14、决代码复用问题。正确使用继承继承分为两种:类对接口的实现,接口继承;类对类的继承,实现继承。实现继承,容易被滥用。对于抽象类,尽量使用合成,而不是继承,来达到复用目的。参考“组合/聚合复用原则”,举例,抽象类4,继承复用的使用场合继承代表“一般化、特殊化”关系,抽象的基类代表一般,而衍生的具体类代表特殊。Peter Coad条件,符合该条件所有内容,才可以使用抽象类:1、子类是超类的一个特殊种类,而不是超类的一个角色。Has-A关系应当使用聚合关系;Is-A关系符合继承关系。Has-A:一个类是另一个类的角色;人-黑社会小弟,老大角色,可以互换:人,可以做黑社会小弟,某时,也可以做老大。Is-

15、A:一个类是另一个类的一种。鸟-麻雀、天鹅种类,不可以互换:鸟,不可以某时是麻雀,某时是天鹅。2、永远不会出现需要将子类换成另一个类的子类的情况。3、子类具有扩展超类的责任,而不具有置换掉、注销掉超类的责任。如果子类需要大量的置换掉超类的行为,则不适合。4、只有在分类学角度上,才可以使用继承,不要从工具类继承,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,里氏代换原则1,孙悟空的以全盖偏西游记第三回:四海千山皆拱伏 九幽十类尽除名 孙悟空被拉去阴司,大打出手,十个冥王忙说弄错了,悟空说消名。悟空亲自检阅,直到那魂字一

16、千三百五十号上,方注着孙悟空名字,乃天产石猴,该寿三百四十二岁,善终。悟空道:“我也不记寿数几何,且只消了名字便罢,取笔过来!”那判官慌忙捧笔,饱掭浓墨。悟空拿过簿子,把猴属之类,但有名者一概勾之。螟下簿子道:“了帐,了帐!今番不伏你管了!”。猛的醒来,乃是南柯一梦。才觉伸腰,只闻得四健将与众猴高叫道:“大王,吃了多少酒,睡这一夜还不醒来?”悟空道:“醒还小可,我梦见两个人来此勾我,把我带到幽冥界城门之外,却才醒悟。是我显神通,直嚷到森罗殿,与那十王争吵,将我们的生死簿子看了,但有我等名号,俱是我勾了,都不伏那厮所辖也。”众猴磕头礼谢。自此,山猴多有不老者,以阴司无名故也。美猴王言毕前事,四健

17、将报知各洞妖王,都来贺喜。不几日,六个义兄弟,又来拜贺,一闻销名之故,又个个欢喜,每日聚乐不题。,里氏代换原则1,孙悟空的以全盖偏美猴王将带“猴”名字的通通删掉,即猴子适用的,石猴、肉猴通通适用。或者说,基类适用的,子类通通适用。这就是里氏代换原则。,里氏代换原则1,里氏代换原则:一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且根本不能察觉出基类对象和子类对象的区别。两个类,一个base类,一个derived类,derived类是base类的子类。若b是基类对象,d是子类对象。如果定义method(base b),则必然可以method(d);反过来,代换不成立。即如果meth

18、od2(derived d),则method2(b)一般不成立。里氏代换原则是继承复用的基石。只有当衍生类可以替换掉基类,软件单元的功能不受影响时,基类才能真正被复用。,里氏代换原则1,JAVA对里氏代换原则的支持:基类Base,对象b;子类Sub,对象d;一般有method(Base b),则可以method(d)问题:基类Base,实现函数public run();子类Sub,是否可以实现函数private run();分析:里氏代换原则要求,凡是基类适用的地方,子类一定适用。因此,子类必须具备基类的所有接口,可以更宽,但不能少。如果method调用了b的public run(),则同样应

19、该可以调用d的run()。但如果Sub中的run定义成了private,不能被调用。此时,就会出错。所以,从里氏代换原则的角度看,Sub中的run不可能被定义为private类型。,里氏代换原则1,JAVA对里氏代换原则的支持的局限性?JAVA编译器,不能检查一个系统在实现和商业逻辑上是否满足里氏代换原则,一个著名的例子,正方形是否是长方形的子类。?,里氏代换原则1,里氏代换原则在设计模式中的体现?-需要深入学习策略模式合成模式代理模式,里氏代换原则-墨子“取譬”,墨子。小取:“白马,马也;乘白马,乘马也;骊马,马也;乘骊马,乘马也。”,里氏代换原则-墨子“取譬”,墨子。小取:“白马,马也;乘

20、白马,乘马也;骊马,马也;乘骊马,乘马也。”马市抽象的基类,白马、黑马都是马的具体子类。一只白马是白马类的实例,一只黑马是黑马类的实例。如果一个方法,适用于马,则必然适用于白马或黑马;如:ridehorse(horse aHorse)成立则ridehorse(aWhiteHorse)成立而ridehorse(aBlackHorse)也成立。即ridehorse接受aWhiteHorse、aBlackHorse作为参数。这种实现,道出了里氏代换原则的精髓。,里氏代换原则发过来不成立,反过来,意味着子类适合的,不是基类适合的。墨子。小取:“盗,人也;恶盗,非恶人也”这里,盗实际是人的角色,而非人的

21、种类,所以算作子类,并不恰当。如果说到种类(子类),则黑奴可以说,“白人,人也,恶白人,非恶人也”;同样的,“乌,鸟也,恶乌,非恶鸟也”。,里氏代换原则代码重构,代码重构,这里指设计模式的重新编写,并不是某个函数的重构。里氏代换原则讲的是基类与子类的关系。只有当这种关系存在时,历史代换关系才存在;反之,不存在。如果两个具体类之间的关系未被了里氏代换原则,则需要根据具体情况,在两种重构方案中选择一种。1、创建一个新的抽象类,作为两个具体类的基类。将具体类A和B的共同行为转移到C中,从而解决A和B行为不一致的问题。,里氏代换原则代码重构,2、将B到A的继承关系改为委派关系?具体参考合成/聚合复用原

22、则-需要继续学习,里氏代换原则代码重构,回过头来,继续看11、创建一个新的抽象类,作为两个具体类的基类。将具体类A和B的共同行为转移到C中,从而解决A和B行为不一致的问题。实例,长方形与正方形的问题。A:长方形;B:正方形;C:四边形。,里氏代换原则代码重构,如果有一个由继承关系组成的等级结构的话,那么在等级结构的树图上面所有的叶节点,都应该是具体类。而所有的树枝节点,都应该是抽象类或接口。,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,依赖倒转原则,实现“开-闭”原则的关键是抽象化,且从抽象化导出具体化的实现如果说

23、开闭原则是面向对象设计的目标的话,依赖倒转原则就是这个面向对象设计的主要机制。依赖倒转原则的主要思想:依赖于抽象,不要依赖于具体。传统过程开发,设计方法倾向于使高层次的模块依赖于低层次的模块;抽象层次依赖于具体层次。抽象层次:包含的是应用系统的商务逻辑和宏观的、对整个系统来说重要的战略性决定,是必然性的体现;具体层次:则含有一些次要的与实现有关的算法和逻辑,以及战术性的决定,带有很大的偶然性选择。具体层次的代码会经常有变动的,不能避免出现错误。,依赖倒转原则,抽象层次依赖于具体层次,使许多具体层次的细节的算法变化立即影响到抽象层次的宏观的商务逻辑,导致微观决定宏观,战术决定战略,偶然决定必然,

24、依赖倒转原则,抽象层是宏观逻辑的、战略决策的、带有必然选择性的,应该决定具体层次的具体算法、战术决策、偶然性选择。依赖倒转原则就是要把错误的依赖关系再倒转过来,依赖倒转原则,复用性和可维护性的倒转传统过程性设计中的复用性:传统的过程性设计中,复用却侧重于具体层次模块的复用,如算法的复用、数据结构的复用、函数库的复用等,都局限于具体层次模块里的复用。较高层次的结构依赖于较低层次的结构,较低层次的结构又进一步依赖于更低层次的结构。如此,较低层次上的修改,往往导致较高层次的修改,直到高层次逻辑的修改。同样,传统的做法,也强调具体层次上的可维护性,包括一个函数、数据结构等的可维护性,而不是高级层次上的

25、可维护性。,依赖倒转原则,复用性和可维护性的倒转面向对象编程中的复用性:从复用的意义上讲,抽象层次有一个应用系统最重要的宏观逻辑,是做战略判断和决策的地方,如此,抽象层次应当是较为稳定的,应当是复用的重点。传统复用侧重于具体模块和细节的复用,因此,“倒转”,是指复用应当将复用的重点放在抽象层次上。如果抽象层次上的模块相对独立于具体层次的模块的话,抽象层次的模块复用就较为容易。同样的,宏观逻辑是维护的重点,而不是相反。高层次的模块时设计者应当复用的,依赖倒转原则,三种耦合关系:两个类之间,可以有三种耦合关系零耦合:两个类没有耦合关系,就称之为零耦合;具体耦合:耦合发生在两个具体的、可实例化的类之

26、间,由一个类对另一个类的直接引用造成。抽象耦合:发生在一个具体类和一个抽象类(或java接口)之间,使两个必须发生关系的类之间存在最大的灵活性。,依赖倒转原则,依赖倒转原则:要求客户端依赖于抽象耦合;表述:细节应当依赖于抽象,抽象不应当依赖于细节;另外一种表述:要针对接口编程,不要针对实现编程。这种表述,更容易体会。针对接口编程:应当使用Java接口和抽象JAVA类进行变量的类型声明、参量的类型声明、方法的返回值类型声明,以及数据类型的转换等。不要针对实现编程:不应当使用具体JAVA类进行变量的类型声明、参量的类型声明、方法返回值类型说明,以及数据类型转换等。要做到针对接口编程,一个具体JAV

27、A类应当只实现JAVA接口和抽象类中声明过的方法,而不应有多余的方法。依赖倒转关系强调一个系统内的实体之间关系的灵活性。基本上,如果设计师希望遵守“开-闭”原则,则倒转依赖关系便是达到此要求的途径。,依赖倒转原则,变量的静态类型和真实类型静态类型:变量被声明时的类型,也叫做明显类型;实际类型:即所引用的对象的真实类型。实例:List employees=new Vector();对于employees,其静态类型是List,实际类型是Vector。,依赖倒转原则,引用对象的抽象类型很多情况下,java程序需要引用一个对象。此时,如果这个对象有一个抽象类型的话,应当使用这个抽象类型作为变量的静态

28、类型。这就是针对接口编程的意义。,依赖倒转原则,一般做法:黑人 aMan=new 黑人();针对接口的定义:人 aMan=new 黑人();二者的区别,前者使用具体类作为变量的类型,后者使用一个抽象类型(接口)作为类型。后者的好处在于:在决定将黑人类型换为白人类型时,改动很少。如:人 aMan=new 白人();如此,程序具有更好的灵活性,因为出去调用构造子的一行语句之外,程序的其他部分感觉不到变化;特别是存在许多其他程序调用此对象的情况下,优势更为明显。结论:只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参量的类型声明、方法返回类型的声明、属性变量的类型声明

29、等。,依赖倒转原则,如何做到依赖倒转原则以抽象方式耦合是依赖倒转原则的关键。由于一个抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以改换成其子类。因此,里氏代换原则是依赖倒转原则的基础。在抽象层次上的耦合虽然灵活,但也会带来额外的复杂性。在具体类发生变化的可能性非常小的情况下,抽象耦合的优势十分有限,此时,使用具体耦合反而更好。如:专门教鹦鹉唱歌的软件,不必使用鸟类做基类。,依赖倒转原则,如何做到依赖倒转原则依赖倒转原则是面向对象设计的核心原则,以依赖倒转原则位指导原则的设计模式包括:工厂方法模式模版方法模式迭代子模式,依赖倒转原则,JAVA对抽象类型的支持JA

30、VA抽象类型:JAVA接口和JAVA抽象类;区别在于JAVA抽象类可以提供某些方法的部分实现,而JAVA接口不可以,这可能是Java抽象类的唯一优点。(抽象类不能实现具体的对象。)如果向一个抽象类加入一个新的具体方法,那么所有的子类型都可以立刻得到这个具体方法。而如果向JAVA接口加入一个新的方法,则所有实现该接口类就不能成功通过编译。因为他们都没有实现这个心声明的方法,这是JAVA接口的一个缺点。抽象类的实现只能由该抽象类的子类给出,而JAVA语言限制一个类只能从最多一个超类继承。抽象类的类型定义效能大打折扣。而一个类可以继承任意多个java接口。这是接口的优越性。,依赖倒转原则,JAVA对

31、抽象类型的支持2从代码重构角度,将一个单独的java具体类重构成一个java接口的实现很容易,只需声明一个java接口,并将重要的方法添加到接口声明中,然后在具体类定义语句后面加上一个合适的实现语句即可但对于具体类,添加一个java抽象类作为超类,如果该具体类已经继承了一个超类,这个新定义的抽象类就必须向上移动,变成超超类。如此循环,往往使得整个体系受到影响。,依赖倒转原则,JAVA对抽象类型的支持2JAVA接口是定义混合类型的理想工具。混合类型:就是在一个类的主类型之外的次要类型。一个混合类型表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。实例,需要补充,依赖倒转原则,联合使用

32、JAVA接口和JAVA抽象类由于JAVA抽象类具有提供缺省模式的优点,而JAVA接口具有其他所有优点,所以联合使用是一种较好的选择:首先,声明类型的工作由java接口承担,但同时给出一个java抽象类,为该接口提供一个缺省实现。其他同属于这个抽象类型的具体类可以实现该接口,也可以选择继承自这个抽象类。如果一个具体类直接实现该java接口,必须自行实现所有的接口内容。而如果他继承自抽象类的话,则可以省去一些不必要的方法,因为可以从抽象类中自动得到这些方法的缺省实现。如果需要向java接口加入一个新的方法的话,那么只需同时向该抽象类中加入这个方法的一个具体实现即可,子类都会从该抽象类中得到该具体方

33、法,而不需要在每个子类中一一实现。参考“缺省适配模式”为什么不能用单一抽象类替代已经集成其他类的子类,可以实现继承该接口。补充实例。,依赖倒转原则,依赖倒转原则的优点:实现困难;对象的创建很可能要使用对象工厂,以避免对具体类的具体引用;此原则的使用,还会导致大量的类。依赖倒转原则原则假定所有的具体类都是会变化的,实际一些具体类是相当稳定,少有变化的。对此,客户完全可以依赖这个具体类,而不必为此引入一个抽象类型。,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,接口隔离原则,接口隔离原则:使用多个专门的接口,比使用单一的

34、总的接口要好。即:一个类对另一个类的依赖性,应当建立在最小的接口上。角色的合理划分接口,一种是JAVA中的Interface结构;一种是一个类型所具有的所有方法特征的集合。对于第二种”接口“,即方法特征的集合,就会从接口引申出类型的划分。即一个接口相当于剧本中的一个角色。而此角色在舞台上,由哪个演员来演,则相当于接口的实现。为避免混淆,本书将这种角色划分的原则叫做角色隔离原则。,接口隔离原则,定制服务将接口理解为狭义的java接口,接口隔离原则讲的就是为同一个角色提供宽窄不同的接口,以对付不同的客户端,即定制服务。有一个角色service以及3个不同的客户端,每一个JAVA接口都仅仅将客户端需

35、要的行为暴露给客户端,而没有将客户端不需要的行为放到接口中。-适配器模式的应用。,接口隔离原则,接口污染:过于臃肿的接口就是对接口的污染。每一个接口都代表一个角色,实现一个接口的对象,在整个生命周期中都扮演该角色。一个符合逻辑的推断,不应当将几个不同的角色交给同一个接口,而应当交给不同的接口。将没有关系的接口合并在一起,形成一个大的臃肿的接口,是对角色和接口的污染。与迪米特法则的关系迪米特法则要求任何一个软件实体,除非绝对必要,不然不要与外界通信。即使必须进行通信,也应当尽量限制通信的广度和深度。定制服务原则拒绝向客户提供不需要的行为,符合迪米特法则。,接口隔离原则,全文检索引擎的系统设计反面

36、例子一个接口负责所有的操作,从搜索功能到建立索引的功能,甚至包括搜索结果集合的功能均在一个接口内提供。该解决方案违反了角色分割原则,把不同功能的接口放在一起,由一个接口给出包括搜索器角色、索引生成器角色以及搜索结果集角色在内的所有角色。不良结果子类需要全部实现这些接口方法,而对于具体子类,这些方法可能是不需要的。由于接口大而全,修改的可能性大大增加。而一旦修改,其所有子类都需要修改,对子类的影响很大。,接口隔离原则,全文检索引擎的系统设计,接口隔离原则,全文检索引擎的系统设计反面例子一个接口负责所有的操作,从搜索功能到建立索引的功能,甚至包括搜索结果集合的功能均在一个接口内提供。该解决方案违反

37、了角色分割原则,把不同功能的接口放在一起,由一个接口给出包括搜索器角色、索引生成器角色以及搜索结果集角色在内的所有角色。不良结果子类需要全部实现这些接口方法,而对于具体子类,这些方法可能是不需要的。增加无必要的重复劳动。由于接口大而全,修改的可能性大大增加。而一旦修改,其所有子类都需要修改,对子类的影响很大。,接口隔离原则,全文检索引擎的系统设计,接口隔离原则,全文检索引擎的系统设计搜索引擎的功能被分割为三个角色搜索器角色、索引生成器角色、搜索结果集角色索引生成器角色:由于索引生成因数据的格式不同而不同,故分为RdbIndexer和FileIndexer两种实现。FileIndexer类代表对

38、诸如*.txt、*.html、*.doc等文件类型的数据生成全文索引,而RdbIndexer则针对关系数据库的数据进行全文索引生成。这两个实现的都是索引生成器角色,即实现同一接口。搜索器角色则是与索引生成器角色完全不同的角色,它提供用户全文搜索功能。用户传进一些关键字,搜索器角色则返回一个ResultSet对象。搜索结果集角色就是ResultSet。它提供给客户对集合进行迭代走访的功能,如first()将光标移到集合的第一个元素;last()将光标移到集合的下一个元素。,接口隔离原则,定制服务:是一个重要的设计原则,意思为:如果客户端仅仅需要某一些方法的话,那么就应该向客户提供这些所需要的方法

39、,而不要提供不需要的方法。优点:结构整洁清晰;系统的可维护性。向客户端提供public接口是一种承诺,一个public接口一旦提供,就很难撤回。对于不必要的过多的承诺,会给系统的维护造成不必要的负担。如果这些接口仅仅是内部使用,将接口隔离,也可以降低维护成本,因为一旦提供的服务出现变化,设计师指导哪些客户端会受到影响,哪些不会。,接口隔离原则,接口隔离原则涉及的设计模式备忘录模式迭代子模式需要落实学习。,面向对象的设计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,合成/聚合复用原则,合成/聚合复用原则,也称合成复用原则:就是在一个

40、新的对象里面使用一些已有的老对象,使之成为新对象的一部分;新的对象通过向这些老对象的委派,达到复用已有功能的目的。该原则的简述:要尽量使用合成/聚合,尽量不要使用继承。合成:合成关系中,新对象在整体角度,对其组成部分拥有完全的支配权,包括他们的创建和湮灭。即组合而成的新对象对组成部分的内存分配、内存释放有绝对的责任。一个合成的多重性不能超过1,即,一个合成关系中的成分对象不能与另外一个合成关系共享。一个合成关系湮灭了,则所有的成分对象在同一时间内都会被湮灭。补充实例,合成/聚合复用原则,合成复用:将已有老对象纳入到新对象中,使之成为新对象的一部分。好处:新对象存取成分对象的唯一方法是通过成分对

41、象的借口。这种复用是黑盒复用,老对象的内部细节,在新对象角度看不到。这种复用支持包装?这种复用所需的依赖较少。每一个新类可以讲焦点集中在一个任务上。该复用可以在运行时间内动态进行,新对象可以动态的引用与成分对象类型相同的对象。一般讲,如果一个角色得到了更多的任务,则可以使用合成关系将新任务委派给合适的对象。缺点:通过这种复用建造的系统会有较多的对象需要管理。,合成/聚合复用原则,继承复用:子类继承基类,从而继承基类的方法。优点:新的实现较为容易,基类的大部分功能可以通过继承关系自动进入子类;修改或扩展继承而来的实现较为容易。缺点继承复用破坏包装,基类的内部细节对子类常常是透明的,继承会将超类的

42、实现细节暴露给子类,白箱复用。如果基类发生变化,子类的实现也不得不发生改变。当基类发生变化时,会像石子引起的水波一样,将变化一圈一圈的传导到一级又一级更大范围的子类中,使得程序员不得不相应的修改这些子类,工作量大。从基类继承而来的实现是静态的,不可能在运行期间发生改变,缺乏灵活性。,合成与继承,人被继承到雇员、资本家、老师、学生。而实际上,这些都只是人的角色,而非人的种类。人往往可以同时有几个不同的角色。如,资本家可以同时是学生,即使老师,也可以同时是其他人的学生。继承,是静态的,会使得一个人在成为雇员后,永远是雇员,而无法成为资本家、老师、学生等。导致角色无法互换,这是不合理的,面向对象的设

43、计原则,可维护性;可复用性;开闭原则接口抽象类里氏代换原则依赖倒转原则接口隔离原则合成/聚合复用原则迪米特法则,迪米特法则,迪米特法则:又称最小知识原则,就是说,一个对象应当对其他对象有尽可能少的了解;该法则是火星登陆软件系统、木星轨道飞船软件系统的指导设计原则;狭义迪米特法则如果两个类不必批次直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另外一个类的某一个方法的话,可以通过第三者转发这个调用。,迪米特法则,某人的朋友圈与陌生人某人与一个朋友组成自己的朋友圈,两个人都需要与一个圈外的陌生人发生相互作用。,迪米特法则,某人与朋友的朋友圈”朋友“与陌生人若是朋友关系,则

44、组成”朋友“的朋友圈,迪米特法则,某人与陌生人某人并不需要与陌生人直接发生联系,此事,迪米特法则建议某人通过朋友与陌生人发生相互作用。,迪米特法则,朋友圈的确定当前对象本身(this);以参量形式传入当前对象方法中的对象;当前对象的实例变量直接引用的对象;当前对象的实例变量若是一个聚集,则聚集中的元素都是朋友。当前对象所创建的对象?任何一个对象,如果满足上述条件之一,就是当前对象的朋友。否则,就是陌生人。,迪米特法则,朋友圈的确定当前对象本身(this);以参量形式传入当前对象方法中的对象;当前对象的实例变量直接引用的对象;当前对象的实例变量若是一个聚集,则聚集中的元素都是朋友。当前对象所创建

45、的对象?任何一个对象,如果满足上述条件之一,就是当前对象的朋友。否则,就是陌生人。,迪米特法则,朋友圈的确定当前对象本身(this);以参量形式传入当前对象方法中的对象;当前对象的实例变量直接引用的对象;当前对象的实例变量若是一个聚集,则聚集中的元素都是朋友。当前对象所创建的对象?任何一个对象,如果满足上述条件之一,就是当前对象的朋友。否则,就是陌生人。,迪米特法则,狭义迪米特法则的缺点狭义迪米特法则,会在系统里造出大量的小方法,散落在系统的各个角落。这些方法仅仅是传递间接地调用,因此与系统的商务逻辑无关。对于开发人员,从一张类图了解总体架构的时候,这些小的方法,往往会造成迷惑和困扰。遵循类之

46、间的迪米特法则,则会使一个系统的局部设计简化。因为每一个局部都不会和远距离的对象有直接的关联。但是,这也会造成系统之间不同模块之间的通讯效率下降,并使系统不同模块之间不易协调。,迪米特法则,迪米特法则与依赖倒转原则互补使用为了克服狭义迪米特法则的缺点,可以使用依赖倒转原则,引入一个抽象的类型引用“抽象陌生人”对象,使“某人”依赖于”抽象陌生人“。即:将”抽象陌生人”变为朋友。,迪米特法则,迪米特法则与依赖倒转原则互补使用某人现在与一个抽象角色建立了朋友关系,这样做的好处是“朋友”可以随时将具体的陌生人替换掉。只要心的具体“陌生人”具有相同的抽象类型,则“某人”就无法区分他们。这就允许“陌生人”

47、的具体实现可以独立于“某人”而变化。,迪米特法则,迪米特法则与设计模式门面模式调停者模式,迪米特法则,广义的迪米特法则迪米特法则所谈论的,就是对象之间信息流量、流向以及信息的影响的控制。在软件系统中,一个模块设计的好不好的最重要、最主要的标志:就是该模块在多大的程度上将自己的内部数据和其他与实现有关的细节隐藏起来。一个设计的好的模块可以将它所有的实现细节隐藏起来,彻底的将提供给外界的API和自己的实现分割开来。这样,模块与模块之间就可以仅仅通过彼此的API相互通信,而不理会模块内部的工作细节。这就是“信息的隐藏”,或曰“封装”。,迪米特法则,广义的迪米特法则封装的意义在于:可以使各个子系统之间

48、脱耦,从而允许他们独立的被开发、优化、使用、阅读、修改。脱耦,由于可以独立的同时开发,因而可以有效的加快系统的开发过程。而且,是维护过程变得容易,而不必担心对其他模块的影响。信息的隐藏,可以促进软件的复用。由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,封装的效能也越明显。,迪米特法则,迪米特法则只要用意是控制信息的过载。实际应用中,需要注意:在类的划分上,应当创建有弱耦合的类。类之间的耦合越弱,越有利于复用。即一个弱耦合的类一旦被修改,不会对有关系的类造成波及。在类的结构设计上,每一个类都应当尽量降低成员的访问权限。

49、即:一个类包装好各自的private内容。这样一来,想要了解一个类的意义,就无需了解很多别的细节。一个类,不应当public自己的属性,而应当提供取值和赋值的方法让外界间接访问自己的属性。在类是设计上,只要有可能,一个类应当设计成不变类。在对其他类的引用上,一个对象对其他对象的引用应当降到最低,迪米特法则,广义的迪米特法则在类的设计上的体现优先将一个类设置为不变类Java API 提供了很多不变类,如String,BigInteger等,不变类易于设计、实现和使用。一个对象与外界的通信大致分为两种,一种是改变这个对象的状态的,另一种不盖被状态。如果一个对象的内部状态根本就是不可能改变的,那么它

50、与外界的通信就大打折扣。当涉及任何一个类时,首先考虑这个类的状态是否需要改变,即一个类必须是可变类,在给它的属性设置赋值方法的时候,要保持谨慎,出发真的必要,否则不要为一个属性设置赋值方法。,迪米特法则,广义的迪米特法则在类的设计上的体现尽量降低一个类的访问权限在满足一个系统对这个类的需求的同时,应当尽量降低这个类的访问权限。对于顶级的类来说,只有两个可能的访问等级。Package-private:默认访问权限。那么就只能从当前库访问。Public,可以从当前库和其他库访问。对于package-private访问权限的类,一旦这个类发生修改,那么受到影响的客户必定局限于这个库内部。由此,可以自

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号