面向对象设计的七大设计原则

    科技2022-08-07  120

    面向对象设计的七大设计原则

    面向对象设计的七大设计原则为:

    开闭原则里氏代换原则迪米特原则(最少知道原则)单一职责原则接口分隔原则依赖倒置原则组合/聚合复用原则。

    七大原则之间并不是相互孤立的,彼此间存在着一定关联,一个可以是另一个原则的加强或是基础。违反其中的某一个,可能同时违反了其余的原则。 开闭原则是面向对象的可复用设计的基石。其他设计原则是实现开闭原则的手段和工具。

    七大原则分成如下两部分:

    设计目标:开闭原则、里氏替换原则、最少知识原则设计方法:单一职责原则、接口隔离原则、依赖倒置原则、组合/聚合原则

    开闭原则(The Open-Closed Principle, OCP)

    定义:软件实体(模块,类,方法等)应该对扩展开放,对修改关闭。

    根据开闭原则,在设计一个软件系统模块(类,方法)的时候,应该可以在不修改原有的模块(修改关闭)的基础上,能扩展其功能(扩展开放)。

    实现:不使用直接依赖来实现功能,而采取接口的形式。

    好处:

    稳定性。开闭原则要求扩展功能不修改原来的代码,这可以让软件系统在变化中保持稳定。扩展性。开闭原则要求对扩展开放,通过扩展提供新的或改变原有的功能,让软件系统具有灵活的可扩展性。遵循开闭原则的系统设计,可以让软件系统可复用,并且易于维护。

    里氏替换原则(LisKov Substitution Principle, LSP)

    定义:所有引用基类的地方必须能透明地使用其派生类的对象。

    不应该在代码中出现if/else之类对派生类类型进行判断的条件。派生类应当可以替换基类并出现在基类能够出现的任何地方。里式替换原则的引申意义:子类可以扩展父类的功能,但不能改变父类原有的功能。 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。子类中可以增加自己特有的方法。当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。当子类的方法实现父类的方法时(重载/重写或实现抽象方法)的后置条件(即方法的输出/返回值)要比父类更严格或相等。

    优点:

    约束继承泛滥,是开闭原则的一种体现。加强程序的健壮性,同时变更时也可以做到非常好地提高程序的维护性、扩展性。降低需求变更时引入的风险。

    重构违反LSP的设计:将继承关系改为关联关系。

    迪米特原则(最少知道原则)(Law of Demeter,LoD)

    定义:只与你的直接朋友交谈,不跟“陌生人”说话。

    一个软件实体应当尽可能少地与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。 如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。

    优点:

    降低类之间的耦合。

    缺点:

    系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系,这在一定程度上增加了系统的复杂度。

    单一职责原则(Single Responsibility Principle,SRP)

    定义:永远不要让一个类存在多个改变的理由。

    如果一个类需要改变,改变它的理由永远只有一个。如果存在多个改变它的理由,就需要重新设计该类。 单一职责原则原则的核心含意是:只能让一个类/接口/方法有且仅有一个职责。

    如果一个类具有一个以上的职责,那么就会有多个不同的原因引起该类变化,而这种变化将影响到该类不同职责的使用者(不同用户):

    一方面,如果一个职责使用了外部类库,则使用另外一个职责的用户却也不得不包含这个未被使用的外部类库。另一方面,某个用户由于某个原因需要修改其中一个职责,另外一个职责的用户也将受到影响,他将不得不重新编译和配置。这违反了设计的开闭原则,也不是我们所期望的。

    优点:

    单一职责原则从职责(改变理由)的侧面上为我们对类(接口)的抽象的颗粒度建立了判断基准:在为系统设计类(接口)的时候应该保证它们的单一职责性。降低了类的复杂度、提高类的可读性,提高系统的可维护性、降低变更引起的风险。

    接口分隔原则(Interface Segregation Principle ,ISP)

    定义:不能强迫用户去依赖那些他们不使用的接口。

    接口的设计原则:接口的设计应该遵循最小接口原则,不要把用户不使用的方法塞进同一个接口里。如果一个接口的方法没有被使用到,则说明该接口过胖,应该将其分割成几个功能专一的接口。接口的依赖(继承)原则:如果一个接口a继承另一个接口b,则接口a相当于继承了接口b的方法,那么继承了接口b后的接口a也应该遵循上述原则:不应该包含用户不使用的方法。 反之,则说明接口a被b给污染了,应该重新设计它们的关系。

    原则:

    一个类对一个类的依赖应该建立在最小的接口上。建立单一接口,不要建立庞大臃肿的接口。尽量细化接口,接口中的方法尽量少。

    实现:

    通过多重继承实现。通过关联来实现。

    优点:

    接口分隔原则从对接口的使用上为我们对接口抽象的颗粒度建立了判断基准:在为系统设计接口的时候,使用多个专门的接口代替单一的胖接口。符合高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性。

    注意适度原则,接口分隔要适度,避免产生大量的细小接口。

    单一职责原则和接口分隔原则的区别:

    单一职责原则的范围更广,强调接口、类、方法的职责是单一的,针对程序的实现细节。接口分隔原则主要是约束接口,更加抽象,针对整体框架。

    依赖倒置原则(Dependency Inversion Principle ,DIP)

    定义:

    高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象 。针对接口编程,不要针对实现编程。

    原则:

    依赖于抽象。任何变量都不应该持有一个指向具体类的指针或引用;任何类都不应该从具体类派生。设计接口而非设计实现。使用继承避免对类的直接绑定;抽象类/接口:倾向于较少的变化;抽象是关键点,它易于修改和扩展;不要强制修改那些抽象接口/类。避免传递依赖。避免高层依赖于低层。

    实现:

    在高层模块与低层模块之间,引入一个抽象接口层。 抽象接口是对低层模块的抽象,低层模块继承或实现该抽象接口。这样,高层模块不直接依赖低层模块,而是依赖抽象接口层。抽象接口也不依赖低层模块的实现细节,而是低层模块依赖(继承或实现)抽象接口。类与类之间都通过抽象接口层来建立关系。

    优点:

    可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险。

    组合/聚合复用原则(Composite/Aggregate Reuse Principle ,CARP)

    定义:尽量使用组合/聚合,不要使用类继承。

    即在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新对象通过向这些对象的委派达到复用已有功能的目的。就是说要尽量的使用合成和聚合,而不是继承关系达到复用的目的。组合和聚合都是关联的特殊种类。聚合表示整体和部分的关系,表示“拥有”。组合则是一种更强的“拥有”,部分和整体的生命周期一样。组合的新的对象完全支配其组成部分,包括它们的创建和湮灭等。一个组合关系的成分对象是不能与另一个组合关系共享的。

    优点:

    新对象存取子对象的唯一方法是通过子对象的接口。这种复用是黑箱复用,因为子对象的内部细节是新对象所看不见的。这种复用更好地支持封装性。这种复用实现上的相互依赖性比较小。每一个新的类可以将焦点集中在一个任务上。这种复用可以在运行时间内动态进行,新对象可以动态的引用与子对象类型相同的对象。作为复用手段可以应用到几乎任何环境中去。

    缺点:系统中会有较多的对象需要管理。

    对比“继承”:

    通过继承来进行复用的: 优点: 新的实现较为容易,因为基类的大部分功能可以通过继承的关系自动进入派生类。修改和扩展继承而来的实现较为容易。 缺点: 继承复用破坏封装性,因为继承将基类的实现细节暴露给派生类。由于基类的内部细节常常是对于派生类透明的,所以这种复用是透明的复用,又称“白箱”复用。如果基类发生改变,那么派生类的实现也不得不发生改变。从基类继承而来的实现是静态的,不可能在运行时间内发生改变,没有足够的灵活性。
    Processed: 0.010, SQL: 8