设计原则
简介:https://www.cnblogs.com/zhili/p/DesignPatternSummery.html
第一篇:https://www.cnblogs.com/shoshana-kong/p/8847893.html
第二篇:https://www.cnblogs.com/www-zsl187-com/p/8821885.html
1、开放 - 封闭原则 ( Open-Closed Principle, OCP )
通俗:对扩展开放,对修改关闭
开闭原则(Open-Closed Principle, OCP)强调的是:一个软件实体(指的类、函数、模块等)应该对扩展开放,对修改关闭。即每次发生变化时,要通过添加新的代码来增强现有类型的行为,而不是修改原有的代码。
符合开闭原则的最好方式是提供一个固有的接口,然后让所有可能发生变化的类实现该接口,让固定的接口与相关对象进行交互。
2、单一职责原则 ( Single Responsibility Principle )
通俗:一个类只做一件事
就一个类而言,应该只有一个引起它变化的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会影响到其他的职责,另外,把多个职责耦合在一起,也会影响复用性。
3、里氏替换原则 ( Liskov Substitution Principle )
通俗:子类不能去修改父类的功能
里氏代替原则(Liskov Substitution Principle, LSP)指的是子类必须替换掉它们的父类型。也就是说,在软件开发过程中,子类替换父类后,程序的行为是一样的。只有当子类替换掉父类后,此时软件的功能不受影响时,父类才能真正地被复用,而子类也可以在父类的基础上添加新的行为。
4、迪米特法则 / 最少知识原则 ( Law Of Demeter )
通俗:高内聚,低耦合
迪米特法则(Law of Demeter,LoD)又叫最少知识原则(Least Knowledge Principle,LKP),指的是一个对象应当对其他对象有尽可能少的了解。也就是说,一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。
关于迪米特法则其他的一些表述有:只与你直接的朋友们通信;不要跟“陌生人”说话。
5、依赖倒置原则 ( Dependence Inversion Principle )
通俗:类似 IOC,采用接口编程
依赖倒置(Dependence Inversion Principle, DIP)原则指的是抽象不应该依赖于细节,细节应该依赖于抽象,也就是提出的 “面向接口编程,而不是面向实现编程”。这样可以降低客户与具体实现的耦合。
6、接口隔离原则 ( Interface Segregation Principle )
通俗:细节接口
接口隔离原则(Interface Segregation Principle, ISP)指的是使用多个专门的接口比使用单一的总接口要好。也就是说不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。过于臃肿的接口是对接口的一种污染。
7、合成 / 聚合原则 ( Composite / Aggregate Reuse Principle, CARP )
通俗:避免使用继承
合成复用原则(Composite Reuse Principle, CRP)就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。
第一篇
开放-封闭原则具有理想主义的色彩,他是面向对象设计的终极目标。其他几条则可以看做是开放-封闭原则的实现方法。设计模式就是实现了这些原则,从而达到了代码复用,增加可维护性的目的。
一.开放 - 封闭原则
概念:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。模块应该尽量在不修改原代码的情况下进行扩展。
在软件周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给代码引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有代码来实现变化。
开放封闭原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现的频繁变化的那些部分作出抽象,然而,对于应用程序中的每个部分都刻意的进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。
注意事项:
1. 通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的 public 方法。
2. 参数类型、引用对象尽量使用接口或者抽象类,而不是实现类
3. 抽象层尽量保持稳定,一旦确定不允许修改。
二.单一职责原则
概念:就一个类而言,应该仅有一个引起它变化的原因。
当我们在做编程的时候,很自然地会一个一个类加上各种各样的功能。这样意味着,无论任何需求要来,你都需要更改这个类,这样其实是很糟糕的,维护麻烦,复用不可能,也缺乏灵活性。如果一个类承担的职责过多,就等于把这些职责耦合起来,一个职责变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭到很多意想不到的破坏。
三.依赖倒转原则
概念:依赖倒转原则是程序要依赖于抽象接口,不要依赖于具体实现。简单的来说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块的耦合。
有时候为了代码复用,一般会把常用的代码写成函数或类库。这样开发新项目的时候直接用就行了。比如做项目的时候大多要访问数据库,所以我们把访问数据库的代码写成了函数。每次做项目去调用这些函数。那么问题来了,我们要做新项目的时候,发现业务逻辑高层模块都是一样的,但客户却希望使用不同的数据库或存储方式,这时就出现了麻烦。我们希望能再次利用这些高层模块,但是高层模块都是与低层的访问数据库绑定在一起,没办法复用这些高层的模块。所以不管是高层模块和底层模块都应该依赖于抽象,具体一点就是接口或者抽象类,只要接口是稳定的,那么任何一个更改都不用担心。
注意事项:
1. 高层模块不应该依赖于低层模块。两个都应该依赖抽象。
2. 抽象不应该依赖细节。细节应依赖于抽象。
四.迪米特法则(也称为最少知识原则)
概念:一个软件实体应当尽可能地少与其他实体发生相互作用。每一个软件单位对其他软件单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。迪米特法则不希望类之间建立直接的联系。如果有真的需要建立联系的,也希望能通过他的友元类来转达。因此,应用迪米特法则有可能造成一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互关系,这在一定程度上增加了系统的复杂度。
五.接口隔离原则
概念:客户端不应该依赖他不需要的接口,类间的依赖关系应建立在最小的接口上。
接口隔离原则的核心定义,不出现臃肿的接口,但是“小”是有限度的,首先就是不能违反单一职责原则。
六.合成 / 聚合复用原则
概念:合成 / 聚合复用原则经常又叫做合成复用原则,就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新的对象通过这些对象的委派达到复用已有功能的目的。他的设计原则是:要尽量使用合成 / 聚合,尽量不要使用继承。
七.里氏代换原则
概念:里氏代换原则是面向对象设计的基本原则之一。即任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受影响时,基类才能被真正复用,而衍生类也能够在积累的基础上增加新的行为,里氏代换原则是对 “ 开 - 闭 ” 原则的补充。实现 “ 开 - 闭 ” 原则的关键步骤就是抽象化。在基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
当满足继承的时候,父类肯定存在非私有的成员,子类肯定是得到了父类的这些非私有成员(假设,父类的成员全部是私有的,那么子类没办法从父类继承任何成员,也就不存在继承的额概念了)。既然子类继承了父类的这些非私有成员,那么父类对象也就可以在子类对象中调用这些非私有成员。所以,子类对象可以替换父类对象的位置。
在里氏代换原则下,当需求有变化时,只需继承,而别的东西不会改变。由于里氏代换原则才使得开放封闭称为可能。这样使得子类在父类无需修改就可以扩展。
第二篇
1. 开闭原则(Open-Closed Principle, OCP)
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
个人解释:软件实体如同你租住的房子一般,你可以向里面添加东西,但是却很难修改这个房间。扩展开放就相当于你向租住的房子里放置家具,充实这个房子的功能。修改关闭就好比是房子的已经存在的物件,道理上你是没有这个改变他们的能力,实际是你在付出代价之后可以更改。但绝对的修改关闭是不可能的。就好比如水龙头、下水管道、灯泡等这些房子存在的基本物件损坏一样,不可避免的,所以需要提前做好准备避免。而在软件中避免就是创建抽象来隔离以后发生同类的变化。
开放-封闭原则,可以保证以前代码的正确性,因为没有修改以前代码,所以可以保证开发人员专注于将设计放在新扩展的代码上。
简单的用一句经典的话来说:过去的事已成历史,是不可修改的,因为时光不可倒流,但现在或明天计划做什么,是可以自己决定(即扩展)的。
2. 单一职责原则(Single Responsibility Principle)
定义:即一个类只负责一项职责,应该仅有一个引起它变化的原因。
个人解释:你有一个带茶漏的茶杯(即类)用来喝茶和喝水(即两种职责)。有一天你想将奶茶倒入这个茶杯,但由于奶茶有珍珠,茶杯有茶漏,为了将珍珠也放入茶杯中,你将茶漏取出(改变了茶杯的功能),此时的茶杯就不能用来喝茶,所以该茶杯的职责也就被改变。为了避免这种改变你准备了茶杯和水杯,喝茶就用茶杯,喝水就用水杯。这就符合单一职责原则,一个类(杯子)只负责一种职责(喝茶或者喝水)。
单一职责的优点:
- 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
- 提高类的可读性,提高系统的可维护性;
- 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都需要遵循这一重要原则。
3. 里氏替换原则(Liskov Substitution Principle)
定义:子类型必须能够替换掉它们的父类型。
个人解释:如果父类型是鸟,子类型是企鹅,在生物学中企鹅归属于鸟,但是企鹅不会飞,在编程的世界中,企鹅就无法归属于鸟,即企鹅不能继承鸟类。
只有当子类可以替换掉父类,软件单位的功能不受影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。
正是有里氏代换原则,使得继承复用成为了可能。正是由于子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就可以扩展,不然还谈什么扩展开放,修改关闭呢?
里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。
它包含以下4层含义:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
4. 迪米特法则(Law Of Demeter)
定义:迪米特法则又叫最少知道原则,即一个对象应该对其他对象保持最少的了解。
解释:迪米特法则其根本思想是强调了类之间的松耦合。类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成影响,也就是说信息的隐藏促进了软件的复用。
软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。而迪米特法则就是解决低耦合的方法。
个人解释:迪米特法则有个简单的方法叫做:只与直接的朋友通信。朋友关系在编程中就是耦合关系,耦合的方式就像现实世界中交朋友一样有多种,例如:依赖,关联,组合,聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
5. 依赖倒置原则(Dependence Inversion Principle)
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。中心思想是面向接口编程。
在实际编程中,我们一般需要做到如下3点:
- 低层模块尽量都要有抽象类或接口,或者两者都有。
- 变量的声明类型尽量是抽象类或接口。
- 使用继承时遵循里氏替换原则。
依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
6. 接口隔离原则(Interface Segregation Principle)
定义:我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
解释:在程序设计中,依赖几个专用的接口要比依赖一个综合的接口更灵活。就好比术业有专攻一样。通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
采用接口隔离原则对接口进行约束时,要注意以下几点:
- 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
- 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
- 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。设计接口的时候,只有多花些时间去思考和筹划,才能准确地实践这一原则。
7. 合成/聚合原则(Composite/Aggregate Reuse Principle,CARP)
定义:尽量的使用合成和聚合,而不是继承关系达到复用的目的。换句话说,就是能用合成/聚合的地方,绝不用继承。
为什么要尽量使用合成/聚合而不使用类继承?
- 对象的继承关系在编译时就定义好了,所以无法在运行时改变从父类继承的子类的实现
- 子类的实现和它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化
- 当你复用子类的时候,如果继承下来的实现不适合解决新的问题,则父类必须重写或者被其它更适合的类所替换,这种依赖关系限制了灵活性,并最终限制了复用性。