面向对象设计中的五大原则

在本单元重点了解如下知识点在本单元重点了解如下知识点q单一职责原则(单一职责原则(SRPSRP)q接口隔离原则接口隔离原则(ISPISP)q依赖倒置原则依赖倒置原则(DIPDIP)q开放开放-封闭封闭”原则原则(OCPOCP)qLiskovLiskov替换原则替换原则(LSPLSP)如何正确地进行类的设计如何正确地进行类的设计 遵守面向对象设计中的遵守面向对象设计中的“五大原则五大原则”面向对象设计和编程概述面向对象设计和编程概述1 1、面向对象设计(、面向对象设计(OODOOD)和)和面向对象编程语言(面向对象编程语言(OOPLOOPL)Object-Oriented Programming LanguageObject-Oriented Programming Language可以提高程可以提高程序的封装性、复用性、可维护性,但仅仅是序的封装性、复用性、可维护性,但仅仅是“可以可以”让我们开始涉猎软件系统设计的思想、原则和模式方让我们开始涉猎软件系统设计的思想、原则和模式方面的内容面的内容 Robert Robert 在在敏捷软件开发敏捷软件开发原则、方法与实践原则、方法与实践一书中总结出了一书中总结出了“单一职责原则单一职责原则”。
让我们先从让我们先从“单一职责原则单一职责原则”开始吧开始吧2 2、能不能真正实现、能不能真正实现OODOOD和和OOPLOOPL所体现出的这些所体现出的这些“优点优点”?取决于设计和编程人员具体取决于设计和编程人员具体“如何做如何做”3 3、那么如何能够达到如此、那么如何能够达到如此“境界境界”?有什么设计思想、设计原则和设计模式吗?有什么设计思想、设计原则和设计模式吗?1 1、什么是、什么是SRP(Single-Responsibility Principle)SRP(Single-Responsibility Principle)一个类应该仅有一个引起它的变化的原因(职责),一个类应该仅有一个引起它的变化的原因(职责),这条原则也称为类设计的这条原则也称为类设计的“高内聚性原则高内聚性原则”少管闲事,专心做一件事少管闲事,专心做一件事情!情!“一心无二用一心无二用”!它指导我们如何提高代码的可重用度它指导我们如何提高代码的可重用度!你认为该你认为该JSPJSP页面页面有什么问题吗?有什么问题吗?单一职责原则单一职责原则(l l)含义之一)含义之一 避免相同的职责(也称为功能)分散到不同的类中实现避免相同的职责(也称为功能)分散到不同的类中实现(2 2)含义之二)含义之二 也应该要避免一个类承担过多的职责。
也应该要避免一个类承担过多的职责2 2、为什么要遵守单一职责原则、为什么要遵守单一职责原则(1 1)可以减少类之间的耦合)可以减少类之间的耦合你知道为什么你知道为什么“螺钉螺钉”的可重用性高!的可重用性高!主要的责任在主要的责任在“方丈方丈”p当需求变化时,只修改一个类,从当需求变化时,只修改一个类,从而也就隔离了变化;而也就隔离了变化;p如果一个类有多个不同的职责,它如果一个类有多个不同的职责,它们就耦合在一起,当一个职责发生变们就耦合在一起,当一个职责发生变化时,可能会影响其它的职责化时,可能会影响其它的职责2 2)不遵守单一职责原则的后果)不遵守单一职责原则的后果p会影响到对该类的复用性会影响到对该类的复用性p当只需要复用该类的某一个职责时,当只需要复用该类的某一个职责时,但由于它和其它的职责耦合在一起,但由于它和其它的职责耦合在一起,也就很难分离出也就很难分离出3 3)遵守单一职责原则的示例)遵守单一职责原则的示例 上面的图示表示,在系统的持久层的设计中,将实现上面的图示表示,在系统的持久层的设计中,将实现数据库连接和数据库访问操作相互分离;由两种不同形式数据库连接和数据库访问操作相互分离;由两种不同形式的类承担。
的类承担而如果将它们耦合在一起(数据库连接、数据库而如果将它们耦合在一起(数据库连接、数据库CRUDCRUD操作和数据库连接释放等),将导致操作和数据库连接释放等),将导致“数据连接数据连接”的职责的职责在被重用时,将出现问题!在被重用时,将出现问题!拔出萝卜带出泥!拔出萝卜带出泥!3 3、遵守单一职责原则的各种、遵守单一职责原则的各种GOFGOF设计模式设计模式类的设计主要工作是类的设计主要工作是“发现发现职责职责”并并“分离职责分离职责”!(2 2)模板方法模式的应用)模板方法模式的应用(1 1)工厂模式的应用)工厂模式的应用 分离对象的分离对象的“创建创建”和对象的和对象的“使用使用”方面方面的职责分离分离 “共性功共性功能实现能实现”和和“个性个性扩展扩展”方方面的职责面的职责3 3)命令模式的应用)命令模式的应用 分离分离“命令的请求者命令的请求者”和和“命令的实现者命令的实现者”方面的职责方面的职责4 4)代理模式的应用)代理模式的应用p分离分离 “服务的请求者服务的请求者”和和“服务的提供者服务的提供者”各自方各自方面的职责;面的职责;p应用业务代理类的主要的目的是降低客户端与业务组应用业务代理类的主要的目的是降低客户端与业务组件之间的紧密关联性和提高业务功能组件类的安全性。
件之间的紧密关联性和提高业务功能组件类的安全性希望大家掌握对这些基希望大家掌握对这些基本的模式的具体应用!本的模式的具体应用!4 4、遵守单一职责原则的系统架构设计、遵守单一职责原则的系统架构设计(1 1)单一职责原则不只是对类设计有意义,对以模块、子)单一职责原则不只是对类设计有意义,对以模块、子系统为单位的系统架构设计同样也有意义系统为单位的系统架构设计同样也有意义 一个模块、子系统也应该仅有一个引起它变化的原一个模块、子系统也应该仅有一个引起它变化的原因,如因,如MVCMVC所倡导的各个层之间的相互分离其实就是所倡导的各个层之间的相互分离其实就是单一单一职责原则在系统总体设计中的应用职责原则在系统总体设计中的应用2 2)Struts2Struts2框架中应用代理模式的示例框架中应用代理模式的示例1 1、接口隔离原则(、接口隔离原则(ISPISP)的具体体现)的具体体现(1 1)一个类对另外一个类的依赖性应当是建立在最小的接)一个类对另外一个类的依赖性应当是建立在最小的接口上口上pISPISP可以达到不强迫客户(接口的使用方)依赖于他们可以达到不强迫客户(接口的使用方)依赖于他们不用的方法不用的方法在接口设计中应该保证,接口的实现类在接口设计中应该保证,接口的实现类应该只呈现为单一职责的角色(遵守应该只呈现为单一职责的角色(遵守SRPSRP原则);原则);pISPISP还可以降低客户之间的相互影响还可以降低客户之间的相互影响当某个客户程当某个客户程序要求提供新的职责(需求变化)而迫使接口发生改变序要求提供新的职责(需求变化)而迫使接口发生改变时,影响到其他客户程序的可能性会最小。
时,影响到其他客户程序的可能性会最小它指导我们如何正确它指导我们如何正确地进行接口设计地进行接口设计!接口隔离原则接口隔离原则(2 2)客户端程序不应该依赖它不需要的接口方法(功能)客户端程序不应该依赖它不需要的接口方法(功能)比如在应用继承时,由于子类将继承父类中的所有可比如在应用继承时,由于子类将继承父类中的所有可用的方法;而父类中的某些方法,在子类中可能并不需要,用的方法;而父类中的某些方法,在子类中可能并不需要,但也将被父类强迫使用?!但也将被父类强迫使用?!因此,谨用继承!因此,谨用继承!2 2、对接口的污染(过于臃肿的接口设计是对接口的污染)、对接口的污染(过于臃肿的接口设计是对接口的污染)(l l)接口的污染()接口的污染(Interface ContaminationInterface Contamination)一个没有经验的设计师往往想节省接口的数目,将一些一个没有经验的设计师往往想节省接口的数目,将一些功能相近或功能相关的接口合并功能相近或功能相关的接口合并2 2)所谓接口污染就是为接口添加了不必要的职责)所谓接口污染就是为接口添加了不必要的职责 如果开发人员在接口中增加一个新的功能方法的主如果开发人员在接口中增加一个新的功能方法的主要目的只是为了要目的只是为了减少接口的实现类的数目减少接口的实现类的数目,如此设计将,如此设计将导致接口被不断地导致接口被不断地“污染污染”并并“变胖变胖”。
3 3)软件系统中类的设计是否合理不在乎类本身的数目)软件系统中类的设计是否合理不在乎类本身的数目p接口污染会给系统带来维护和重用等方面的问题接口污染会给系统带来维护和重用等方面的问题p为了能够重用被污染的接口,接口的实现类就被迫要为了能够重用被污染的接口,接口的实现类就被迫要实现并维护不必要的功能方法实现并维护不必要的功能方法3 3、如何避免不良好的设计、如何避免不良好的设计(1 1)每个具体的功能实现类都应该对应有一个专用的接口)每个具体的功能实现类都应该对应有一个专用的接口 因为接口隔离原则其实是单一职责应用于接口设计上因为接口隔离原则其实是单一职责应用于接口设计上的细化准侧,请参考如下的示例:的细化准侧,请参考如下的示例:(2 2)“接口隔离接口隔离”其实就是定制化服务设计的原则其实就是定制化服务设计的原则 (3 3)使用接口的多重继承实现对不同的接口的组合,从使用接口的多重继承实现对不同的接口的组合,从而对外提供组合功能而对外提供组合功能达到达到“按需提供服务按需提供服务”4 4、如何改进、如何改进“胖接口胖接口”的设计结果的设计结果 如果现有的系统已经设计成胖接口,可以使用适配器如果现有的系统已经设计成胖接口,可以使用适配器模式隔离它。
如下示例:模式隔离它如下示例:5 5、为什么要遵守接口隔离原则、为什么要遵守接口隔离原则可以获得更灵活的设计可以获得更灵活的设计1 1、依赖倒置、依赖倒置(1 1)什么是依赖倒置)什么是依赖倒置-将依赖关系倒置为依赖接口将依赖关系倒置为依赖接口 它指导我们如何正确地消解模块间的依赖它指导我们如何正确地消解模块间的依赖关系关系!同时它也是框架设计的核心原则同时它也是框架设计的核心原则p上层模块不应该依赖于下层模块,它们共同依赖于一上层模块不应该依赖于下层模块,它们共同依赖于一个抽象(父类不能依赖子类,它们都要依赖抽象类)个抽象(父类不能依赖子类,它们都要依赖抽象类)p抽象不能依赖于具体,具体应该要依赖于抽象抽象不能依赖于具体,具体应该要依赖于抽象依赖倒置原则(依赖倒置原则(Dependency Inversion Principle Dependency Inversion Principle)(2 2)如何消解两个模块间的依赖关系)如何消解两个模块间的依赖关系应该在两个模块之间定义一个抽象接口,上层模块调应该在两个模块之间定义一个抽象接口,上层模块调用抽象接口中定义的方法,下层模块实现该接口的方法。
用抽象接口中定义的方法,下层模块实现该接口的方法3 3)为什么要依赖接口)为什么要依赖接口p因为接口体现对问题的抽象,同时由于抽象一般是相因为接口体现对问题的抽象,同时由于抽象一般是相对稳定的或者是相对变化不频繁的,而具体是易变的对稳定的或者是相对变化不频繁的,而具体是易变的p因此,依赖抽象是实现代码扩展和运行期内绑定(多因此,依赖抽象是实现代码扩展和运行期内绑定(多态)的基础态)的基础只要实现了该抽象类的子类,都可以只要实现了该抽象类的子类,都可以被类的使用者使用被类的使用者使用 2 2、如何满足、如何满足DIPDIP:面向接口编程:面向接口编程(1 1)每个较高层次类)每个较高层次类都为它所需要的服务提都为它所需要的服务提出一个接口声明,较低出一个接口声明,较低层次类实现这个接口层次类实现这个接口2 2)每个高层类都通)每个高层类都通过该抽象接口使用服务过该抽象接口使用服务以前传统的过程设计中是以前传统的过程设计中是从上到下的一条依赖从上到下的一条依赖 满足满足DIPDIP的设计方案的设计方案3 3、对系统设计的要求、对系统设计的要求(1 1)在进行业务设计时)在进行业务设计时 应尽量在接口或抽象类中定义业务方法的原型(功能要应尽量在接口或抽象类中定义业务方法的原型(功能要求),通过具体的实现类(或者子类)来实现该业务方法。
求),通过具体的实现类(或者子类)来实现该业务方法2 2)这样的设计方案的主要优点体现)这样的设计方案的主要优点体现 在对业务方法的具体实现进行修改时,将不会影响到运在对业务方法的具体实现进行修改时,将不会影响到运行时对该业务方法的调用者的程序行时对该业务方法的调用者的程序1 1、什么是、什么是OCP(Open-Close Principle)OCP(Open-Close Principle)原则原则 随着软件系统的规模不断地增大,从而对软件系统的维随着软件系统的规模不断地增大,从而对软件系统的维护和修改带来新的复杂性,这种困境促使法国工程院院士护和修改带来新的复杂性,这种困境促使法国工程院院士Bertrand MeyerBertrand Meyer在在19981998年提出了年提出了“开放开放-封闭封闭”原则,这条原则,这条原则的基本思想是:原则的基本思想是:它指导我们如何提高代码它指导我们如何提高代码的可扩展性的可扩展性!开放开放封闭原则封闭原则 换句话说,也就是要求开发人员可以在不修改系统中现有的功换句话说,也就是要求开发人员可以在不修改系统中现有的功能代码(源代码或者二进制代码、模块的二进制可执行版本,无论能代码(源代码或者二进制代码、模块的二进制可执行版本,无论是可链接的库、是可链接的库、DLLDLL或者或者JavaJava的的.jar.jar文件)的前提下,而实现对应文件)的前提下,而实现对应用系统的软件功能进行扩展。
用系统的软件功能进行扩展这能够做到吗?这能够做到吗?(1 1)Open(Open for extension)Open(Open for extension)模块的行为必须是开放的、支持扩展的,而不是僵化的模块的行为必须是开放的、支持扩展的,而不是僵化的2 2)Closed(Closed for modification)Closed(Closed for modification)在对模块的功能进行扩展时,不应该影响或大规模地影响已在对模块的功能进行扩展时,不应该影响或大规模地影响已有的程序模块有的程序模块3 3、为什么要遵守、为什么要遵守“开放开放-封闭封闭”原则原则 其目的是能够提高系统的可扩展性和可维护性其目的是能够提高系统的可扩展性和可维护性4 4、如何遵守、如何遵守“开放开放-封闭封闭”原则原则(1 1)在设计方面充分应用)在设计方面充分应用“抽象抽象”和和“封装封装”的思想的思想p一方面也就是要在软件系统中找出各种可能的一方面也就是要在软件系统中找出各种可能的“可变因可变因素素”,并将之封装起来;,并将之封装起来;p另一方面,一种可变性因素不应当散落在多个不同代码模另一方面,一种可变性因素不应当散落在多个不同代码模块中,而应当被封装到一个对象中。
块中,而应当被封装到一个对象中你应该了解你应该了解OOOO中的中的“封装封装”特性!特性!2 2、以生活中的示例来说明、以生活中的示例来说明OCPOCP原则的基本思想原则的基本思想(1 1)PCPC电脑功能扩展方式电脑功能扩展方式(2 2)添加)添加“机顶盒机顶盒”实现将实现将“模拟电视模拟电视”扩展为扩展为“数字电数字电视视”(2 2)在系统功能编程实现方面应用面向接口编程)在系统功能编程实现方面应用面向接口编程p当需求发生变化时,可以提供该接口的新的实现类,当需求发生变化时,可以提供该接口的新的实现类,以求适应变化以求适应变化p 面向接口编程要求功能类实现接口、对象声明为接面向接口编程要求功能类实现接口、对象声明为接口类型1 1、LiskovLiskov替换原则(替换原则(LSP-The The Liskov LSP-The The Liskov Substitution PrincipleSubstitution Principle)的定义和主要的思想)的定义和主要的思想 子类型必须能够替换掉它们的父类型、并出现在父类能子类型必须能够替换掉它们的父类型、并出现在父类能够出现的任何地方。
够出现的任何地方它指导我们如何正确地进行继承它指导我们如何正确地进行继承与派生,并合理地重用代码与派生,并合理地重用代码!LiskovLiskov替换原则替换原则3 3、我们应该要思考如下的一些问题、我们应该要思考如下的一些问题(1 1)如何正确地进行继承方面的设计?)如何正确地进行继承方面的设计?(2 2)最佳的继承层次如何获得?)最佳的继承层次如何获得?(3)3)怎样避免所设计的类层次陷入不符合怎样避免所设计的类层次陷入不符合OCPOCP原则的状况?原则的状况?2 2、LSPLSP主要是针对继承的设计原则主要是针对继承的设计原则 因为继承与派生是因为继承与派生是OOPOOP的一个主要特性,能够减少代的一个主要特性,能够减少代码的重复编程实现,从而实现系统中的代码复用!但如何码的重复编程实现,从而实现系统中的代码复用!但如何正确地进行继承设计和合理地应用继承机制呢?正确地进行继承设计和合理地应用继承机制呢?继承与派生有哪些优缺点?继承与派生有哪些优缺点?我们必须要有原则的指导,我们必须要有原则的指导,LiskovLiskov替换原则被提出替换原则被提出4 4、如何遵守该设计原则、如何遵守该设计原则(1 1)父类的方法都要在子类中实现或者重写,并且派生类)父类的方法都要在子类中实现或者重写,并且派生类只实现其抽象类中声明的方法,而不应当给出多余的方法只实现其抽象类中声明的方法,而不应当给出多余的方法定义或实现。
定义或实现2 2)在客户端程序中只应该使用父类对象而不应当直接使)在客户端程序中只应该使用父类对象而不应当直接使用子类对象,这样将可以实现运行期绑定(动态多态)用子类对象,这样将可以实现运行期绑定(动态多态)5 5、代码示例、代码示例6 6、不遵守、不遵守LiskovLiskov替换原则的示例替换原则的示例总结总结:一个系统或子系统要拥有良好的扩展性和实现运行期内:一个系统或子系统要拥有良好的扩展性和实现运行期内绑定(可互换性),有两个必要条件,并且这两个条件缺一不可绑定(可互换性),有两个必要条件,并且这两个条件缺一不可p第一是依赖倒置原则第一是依赖倒置原则-面向接口编程实现类;面向接口编程实现类;p第二是李氏替换原则第二是李氏替换原则-正确地进行继承关系的设计正确地进行继承关系的设计7 7、子类如何、子类如何“个性化个性化”自己的功能实现自己的功能实现(1 1)应该采用在子类中重写基类的方法的实现形式,而不应该采用在子类中重写基类的方法的实现形式,而不是采用新增新的方法的实现形式是采用新增新的方法的实现形式(2)代码示例)代码示例为了能够真正理解前面的为了能够真正理解前面的“五个基本原则五个基本原则”,现在我们通过示例学习如何在实际的编程中应用现在我们通过示例学习如何在实际的编程中应用面向对象设计中的五大原则。
面向对象设计中的五大原则该示例应用的场景该示例应用的场景 利用利用JDBCJDBC编程实现数据库连接编程实现数据库连接1 1、从面向对象的角度来看类之间一般会存在如下关系、从面向对象的角度来看类之间一般会存在如下关系关联关联(Association)(Association)、依赖、依赖(Dependency)(Dependency)、聚合、聚合(Aggregation)(Aggregation)、组合(、组合(CompositionComposition)、泛化)、泛化(Generalization)(Generalization)类之间的各种可能的关系类之间的各种可能的关系2 2、设计人员如何致力于降低类之间关系的藕合度、设计人员如何致力于降低类之间关系的藕合度(1 1)分离系统中各个模块类的接口定义和接口的具体)分离系统中各个模块类的接口定义和接口的具体功能实现功能实现(3 3)尽可能针对抽象编程而不针对具体子类编程)尽可能针对抽象编程而不针对具体子类编程(4 4)应用控制反转()应用控制反转(IoCIoC)设计模式)设计模式(5 5)应用依赖注入()应用依赖注入(DIDI)技术)技术(2 2)利用接口类)利用接口类型的对象作为各个型的对象作为各个层之间或者两个类层之间或者两个类之间的连接点之间的连接点。