设计模式七大原则

什么是高内聚?

   高内聚就是提高接口、类、模块的处理能力,减少对外的交互。具体到接口隔离原则就是,要求在接口中尽量少公布public方法,接口是对外的
承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本。

模块之间存在依赖,关系越紧密, 耦合越强, 模块独立性越差。
模块内部的元素, 关联性越强, 则内聚越高, 模块单一性更强

设计模式目的

为了让程序(软件)具有更好的代码重用性、可读性、可扩展性、可靠性,使代码呈现高内聚、低耦合的特性。

1.代码重用性:相同功能的代码,不用多次编写。
2.可读性:编程规范性,便于阅读和理解。
3.可扩展性(可维护性):增加新功能时非常方便。
4.可靠性:当增加新功能后,对原有功能没有影响。
5.内聚:模块或关注点内部的一系列的相关功能的相关程度的高低。越高越好。
6.耦合:用来度量模块与模块直接的依赖关系。越低越好。

设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)

一、单一职责原则(Single Responsibility Principle)

1.概念(强调的是职责,站在业务逻辑的角度)

   一个类或者模块只负责完成一个职责。通俗的来说就是一个模块、类、方法不要承担过多的任务。
   一个类被改变的原因不能超过一个,也就是说,一个类只有一个职责,如果职责过多,代码就会臃肿,可读性更差,也更难以维护。
   单一职责原则和接口隔离原则有一定的关系,接口隔离以后,职责就单一了,实现这个接口的类的职责自然也就单一了。但是接口隔离关注的
是抽象层,单一职责关注的是两者兼而有之,偏重于实现。

2.为什么要遵守单一职责原则

1、提高类的可维护性和可读写性

一个类的职责少了,复杂度降低了,代码就少了,可读性也就好了,可维护性自然就高了。

2、提高系统的可维护性

系统是由类组成的,每个类的可维护性高,相对来讲整个系统的可维护性就高。当然,前提是系统的架构没有问题。

3、降低变更的风险

一个类的职责越多,变更的可能性就更大,变更带来的风险也就越大。

3.如何遵守单一职责原则

  合理的职责分解相同的职责放到一起,不同的职责分解到不同的接口和实现中去,这个是最容易也是最难运用的原则,关键还是要从业务出发,
从需求出发,识别出同一种类型的职责。举个例子:人的行为分析,包括了生理、生活和工作等行为的分析,生理行为包括吃、跑、睡等行为,
生活行为包括穿衣等行为,工作行为包括上下班,开会等行为。

4.最佳实践

  在实际工作中,有一个经常会用到的设计模式,DAO模式,又叫数据访问对象,里面定义了数据库中表的增、删、改、查操作,按照单一职责原则,
为什么不把增、删、改、查分别定义成四种接口?这是因为数据库的表操作,基本上就是这四种类型,不可能变化,所以没有必要分开定义,反而
经常变化的是数据库的表结构,表结构一变,这四种操作都要跟着变。所以通常我们会针对一张表实现一个DAO,一张表就代表一种类型的职责。

二、接口隔离原则(Interface Segregation Principle)

1.概念(强调接口的方法尽量少)

一个类对另一个类的依赖必须建立在最小接口上。
要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。客户端不应该被迫依赖于它不使用的方法。
要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

2.接口隔离原则和单一职责区别

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

3.接口隔离原则的优点

接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。

1.将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
2.接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
3.如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
4.使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
5.能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

4.接口隔离原则的实现方法

在具体应用接口隔离原则时,应该根据以下几个规则来衡量。

接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

三、依赖倒转原则(Dependency Inversion Principle)

1.概念

高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖其抽象。
换言之,要针对接口编程,而不是针对实现编程。

2.依赖(或者耦合)关系的种类

零耦合(Nil Coupling)关系:如果两个类没有耦合关系,就称之为零耦合。
具体耦合(Concrete Coupling)关系:具体性耦合发生在两个具体的(可实例化的)类之间,经由一个类对另一个具体类的直接引用造成。
抽象耦合(Abstract Coupling)关系:抽象耦合关系发生在一个具体类和一个抽象类(或者Java/C#接口)之间,使两个必须发生关系的类之间存有最大的灵活性。以抽象方式耦合是依赖倒转原则的关键。

依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。

3.变量的静态类型和实际类型:

变量被声明时的类型叫做变量的静态类型。
变量所引用的对象的真实类型叫做变量的实际类型。
List employees = new Vector();
如上代码,employees变量的静态类型是List,而实际类型是Vector。
则尽量不要使用声明语句: Vector employees = new Vector();不要使用一个具体类作为变量的类型,要用抽象类型作为类型,程序能具有更好的灵活性。
只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参量的类型声明、方法返还类型的声明、属性变量的类型声明等。

在实现依赖倒转原则时,我们需要针对抽象层编程,而将具体类的对象通过依赖注入(DependencyInjection, DI)的方式注入到其他对象中。
依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。

4.常用的注入方式有三种,分别是:

构造注入:是指通过构造函数来传入具体类的对象。
设值注入(Setter注入):是指通过Setter方法来传入具体类的对象。
接口注入:是指通过在接口中声明的业务方法来传入具体类的对象。

这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象(父类接收子类)。

开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下,这三个设计原则会同时出现,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。

四、里氏替换原则(Liskov Substitution Principle)

1.概念

所有引用基类的地方必须能够透明的使用其子类对象。

2.核心思想

在程序当中,如果将一个父类对象替换成它的子类对象后,该程序不会发生异常。

3.使用方式

里氏代换原则是开闭原则的重要方式之一,在程序中尽量使用父类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

4.实现要求

子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

五、开闭原则(Open Closed Principle)

1.概念

一个软件实体,如类、模块、函数等部分,应该是对扩展开放、对修改关闭的。用抽象构建框架,用实现扩展细节。

2.开闭原则的作用

开闭原则是面向对象程序设计的终极目标,它使软件实体拥有一定的适应性和灵活性的同时具备稳定性和延续性。具体来说,其作用如下。

1. 对软件测试的影响

软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。

2. 可以提高代码的可复用性

粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。

3. 可以提高软件的可维护性

遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。

3.开闭原则的实现方法

可以通过“抽象约束、封装变化”来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

六、迪米特原则(Law of Demeter)

1.概念

   最小知道原则,即一个类对于自己依赖的类知道的越少越好。也就是说,不管依赖的类多么复杂,都要尽量将他的成员变量、方法封装到其类里,
只对外提供public方法就可以了。
   另一种理解:只和直接朋友通信。直接朋友是指被依赖的类B出现在了类A的成员变量、方法参数、返回值里,就称A是B的朋友。出现在方法体内部的类不属于朋友类。

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

2.理解迪米特法则

不该有直接依赖关系的类之间,不要有依赖;
有依赖关系的类之间,尽量只依赖必要的接口。
迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。

3.如何理解“高内聚、松耦合”?

“高内聚、松耦合”是一个非常重要的设计思想,能够有效提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。

所谓高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一类中。
相近的功能往往会被同时修改,放到同一个类中,修改会比较集中。
所谓松耦合指的是,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类的代码改动。

七、合成复用原则(Composite Reuse Principle)

1.概念

尽量使用合成/聚合的方式,而不是继承。
合成复用原则要求在实现代码复用时,尽量先使用组合或聚合等关联关系,其次考虑使用继承关系。

2.核心思想

将不同的类、模块或组件组合在一起,来创建新的类或对象。
尽量不通过继承已有的类来获得需要的功能。

3.合成复用原则的由来

在早期的面向对象编程中,代码复用主要用继承。通过继承,一个类可以从另一个类派生,获得其属性和方法。

但继承也有一些缺点:

紧耦合(Tight Coupling):继承引入了强耦合,使得子类依赖于父类的内部实现细节。当父类发生变化时,子类会受到影响随之变化。
继承层次复杂性:随着继承层次的增加,代码的复杂性也会增加,继承层次的扩展和维护会很复杂。
白箱复用:继承会将父类的实现细节暴露给子类,父类对子类是透明的,破坏了类的封装性。
限制了复用的灵活性:从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

为了解决继承带来的问题,合成复用原则应运而生。通过合成复用,我们可以更加优雅地实现代码复用。

组合的优点:

低耦合性:代码之间的关联性较小,减少了类之间的紧耦合关系。
黑箱复用:维持了类的封装性,成分对象的实现细节对新对象不可见。
复用灵活度更高:由于不受继承关系的限制,可以适应不同的情况和需求。

确保了每个组件的单一职责原则:当一个组件需要进行更改时,只需要关注特定的组件,不需要影响整个继承层次。

4.组合与继承如何选型

组合并没有完全替代继承,组合与继承分别适用于不同的场景。

4.1 组合

通常用于:需求不断变化的场景中,同时不受继承关系的限制。
在类中使用其他类的功能,但不用继承它们的所有属性和方法。

4.2 继承

通常用于:扩展现有类的功能。
是否使用继承,需要遵循里氏替换原则、Coad 法则。
4.2.1 里氏替换原则
里氏替换原则要求子类对象必须能够替换其父类对象,子类应该保持与父类一致的接口和行为,不应该改变或破坏继承来的约定。
4.2.2 Coad 法则

Coad 法则明确了继承的具体使用条件:

(1)子类是父类的特殊种类,而不是父类的一个角色。仅 is-a 关系适合继承,而 has-a 关系应使用组合来描述。
(2)不会出现需要将子类替换成另一个子类的情况。如果不能确定将来是否需要这种替换,就不应使用继承。
(3)类应扩展而不是替代父类的责任。如果子类需要大规模替代父类的行为,那么这个类不应是父类的子类。
(4)只有在分类学意义上才可以使用继承,不要从工具类继承。

当 Coad 法则中的条件全部被满足时,才应当使用继承关系。

本文总结:

核心思想

找出应用中的可能需要变化之处,将它们独立出来,不要和不变的混在一起;针对接口编程,而不是针对实现编程。

归类

针对面向接口编程:单一职责原则、接口隔离原则、依赖倒转原则。通过接口使类之前的复用结构和抽象结构更清晰

针对继承关系:里氏替换原则

针对扩展性:开闭原则

降低耦合性:迪米特原则、合成复用原则
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值