设计模式(一)设计模式遵循的七大原则
来源:程序员人生 发布时间:2017-03-01 09:00:28 阅读次数:3131次
原文来自搬砖工,如需转载请注明出处
最近几年来,人们积极的提倡和使用设计模式,其根本缘由就是为了实现代码的复用性,增加代码的可保护性。设计模式的实现遵守了1些原则,从而到达代码的复用性及增加可保护性的目的,设计模式对理解面向对象的3大特点有很好的启发,不看设计模式,很难深层地体会到面向对象开发带来的好处 。在刚开始学习中,很难做到将这些模式融汇贯通,所以这个需要我们在编码前多思考,等想充分了,在开始实践编码。下面是设计模式应当遵守的7大原则
1、开闭原则(Open Close Principle)
定义:1个软件实体如类、模块和函数应当对扩大开放,对修改关闭。
开放-封闭原则的意思就是说,你设计的时候,时刻要斟酌,尽可能让这个类是足够好,写好了就不要去修改了,如果新需求来,我们增加1些类就完事了,原来的代码能不动则不动。这个原则有两个特性,1个是说“对扩大是开放的”,另外一个是说“对更改是封闭的”。面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。这就是“开放-封闭原则”的精神所在
比如,刚开始需求只是写加法程序,很快在client类中完成后,此时变化没有产生,需求让再添加1个减法功能,此时会发现增加功能需要修改原来这个类,这就背背了开放-封闭原则,因而你就应当斟酌重构程序,增加1个抽象的运算类,通过1些面向对象的手段,如继承、动态等来隔离具体加法、减法与client耦合,需求仍然可以满足,还能应对变化。此时需求要添加乘除法功能,就不需要再去更改client及加减法类,而是增加乘法和除方法类便可。
绝对的修改关闭是不可能的,不管模块是多么的‘封闭‘,都会存在1些没法对之封闭的变化,既然不可能完全封闭,设计人员必须对他设计的模块应当对哪一种变化封闭做出选择。他必须先猜想出最有可能产生的变化种类,然后构造抽象来隔离那些变化。在我们最初编写代码时,假定变化不会产生,当变化产生时,我们就创建抽象来隔离以后产生同类的变化。
我们希望的是在开发工作展开不久就知道可能产生的变化,查明可能产生的变化所等待的时候越长,要创建正确的抽象就越困难。开放-封闭原则是面向对象设计的核心所在,遵守这个原则可以带来面向对象技术所宣称的巨大好处,也就是可保护、可扩大、可复用、灵活性好。开发人员应当仅对程序中显现出现频繁变化的那些部份做出抽象,但是对利用程序中的每一个部份都刻意地进行抽象一样不是1个好主张,谢绝不成熟的抽象和抽象本身1样重要。开放-封闭原则,可以保证之前代码的正确性,由于没有修改之前代码,所以可以保证开发人员专注于将设计放在新扩大的代码上。
简单的用1句经典的话来讲:过去的事已成历史,是不可修改的,由于时光不可倒流,但现在或明天计划做甚么,是可以自己决定(即扩大)的。
2、里氏代换原则(Liskov Substitution Principle)
定义1:如果对每个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行动没有产生变化,那末类型 T2 是类型 T1 的子类型。
定义2:子类型必须能够替换掉它们的父类型。
描写:1个软件实体如果使用的是1个父类的话,那末1定适用于其子类,而且它发觉不出父类对象和子类对象的区分,也就是说,在软件里面,把父类都替换成它的子类,程序的行动没有变化
例子:在生物学分类上,企鹅是1种鸟,但在编程世界里,企鹅却不能继承鸟。在面向对象设计时,子类具有父类所有非private的行动和属性,鸟会飞,但企鹅不会飞,所以企鹅不能继承鸟类。
只有当子类可以替换掉父类,软件单位的功能不受影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行动,正是有里氏代换原则,使得继承复用成了可能。正是由于子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就能够扩大,不然还谈甚么扩大开放,修改关闭呢
里氏替换原则通俗的来说就是:子类可以扩大父类的功能,但不能改变父类原本的功能。它包括以下
4层含义
1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
2.子类中可以增加自己独有的方法。
3.当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
看上去很不可思议,由于我们会发现在自己编程中常常会违背里氏替换原则,程序照样跑的好好的。所以大家都会产生这样的疑问,假设我非要不遵守里氏替换原则会有甚么后果?
后果就是:你写的代码出问题的概率将会大大增加。
3、依赖倒转原则(Dependence Inversion Principle)
定义:高层模块不应当依赖低层模块,2者都应当依赖其抽象;抽象不应当依赖细节;细节应当依赖抽象。即针对接口编程,不要针对实现编程
依赖倒转其实就是谁也不要依托谁,除约定的接口,大家都可以灵活自若。依赖倒转可以说是面向对象设计的标志,用哪一种语言来编写程序不重要,如果编写时斟酌的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或接口,那就是面向对象的设计,反之那就是进程化的设计了。如果设计的各个部件或类相互依赖,这样就是耦合度高,难以保护和扩大,这也就体现不出面向对象的好处了。
依赖倒转原则,好比1个团队,有需求组,开发组,测试组,开发组和测试组都是面对一样的需求后,做自己相应的工作,而不应当是测试组依照开发组理解的需求去做测试用例,也就是说开发组和测试组都是直接面向需求组工作,大家的目的是1样的,保证产品按时上线,需求是不依赖于开发和测试的。
依赖颠倒原则基于这样1个事实:相对细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类,使用接口或抽象类的目的是制定好规范和契约,而不去触及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
依赖颠倒原则的中心思想是面向接口编程,传递依赖关系有3种方式,以上的说的是是接口传递,另外还有两种传递方式:构造方法传递和setter方法传递,相信誉过spring框架的,对依赖的传递方式1定不会陌生。
在实际编程中,我们1般需要做到以下3点:
1.低层模块尽可能都要有抽象类或接口,或二者都有。
2.变量的声明类型尽可能是抽象类或接口。
3.使用继承时遵守里氏替换原则。
总之,依赖颠倒原则就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖颠倒。
4、接口隔离原则(Interface Segregation Principle)
接口隔离原则的含义是:建立单1接口,不要建立庞大臃肿的接口,尽可能细化接口,接口中的方法尽可能少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立1个很庞大的接口供所有依赖它的类去调用。在程序设计中,依赖几个专用的接口要比依赖1个综合的接口更灵活。接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的分散,提高系统的灵活性和可保护性。
说到这里,很多人会觉的接口隔离原则跟单1职责原则很相似,其实不然。其1,单1职责原则原重视的是职责;而接口隔离原则重视对接口依赖的隔离。其2,单1职责原则主要是束缚类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要束缚接口接口,主要针对抽象,针对程序整体框架的构建。
采取接口隔离原则对接口进行束缚时,要注意以下几点:
1. 接口尽可能小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果太小,则会造成接口数量过量,使设计复杂化。所以1定要适度。
2. 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为1个模块提供定制服务,才能建立最小的依赖关系。
3. 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
应用接口隔离原则,1定要适度,接口设计的过大或太小都不好。设计接口的时候,只有多花些时间去思考和筹划,才能准确地实践这1原则。
5、组合/聚合复用原则
就是说要尽可能的使用合成和聚合,而不是继承关系到达复用的目的
该原则就是在1个新的对象里面使用1些已有的对象,使之成为新对象的1部份:新的对象通过向这些对象的委派到达复用已有功能的目的。
其实这里终究要的地方就是辨别“has-a”和“is-a”的区分。相对合成和聚合,
继承的缺点在于:父类的方法全部暴露给子类。父类如果产生变化,子类也得产生变化。聚合的复用的时候就对另外的类依赖的比较的少。
1.合成/聚合复用
优点:
新对象存取成份对象的唯1方法是通过成份对象的接口;
这类复用是黑箱复用,由于成份对象的内部细节是新对象所看不见的;
这类复用支持包装;
这类复用所需的依赖较少;
每个新的类可以将焦点集中在1个任务上;
这类复用可以在运行时动态进行,新对象可使用合成/聚合关系将新的责任委派到适合的对象。
缺点:
通过这类方式复用建造的系统会有较多的对象需要管理。
2.继承复用
优点:
新的实现较为容易,由于基类的大部份功能可以通过继承关系自动进入派生类;
修改或扩大继承而来的实现较为容易。
缺点:
继承复用破坏包装,由于继承将基类的实现细节暴露给派生类,这类复用也称为白箱复用;
如果基类的实现产生改变,那末派生类的实现也不能不产生改变;
从基类继承而来的实现是静态的,不可能在运行时产生改变,不够灵活。
6、迪米特法则(Law Of Demeter)
迪米特法则其根本思想,是强调了类之间的松耦合,类之间的耦合越弱,越有益于复用,1个处在弱耦合的类被修改,不会对有关系的类造成影响,也就是说,信息的隐藏增进了软件的复用。
自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。不管是面向进程编程还是面向对象编程,只有使各个模块之间的耦合尽可能的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎样样编程才能做到低耦合呢?那正是迪米特法则要去完成的。
迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来说,就是1个类对自己依赖的类知道的越少越好。也就是说,对被依赖的类来讲,不管逻辑多么复杂,都尽可能地的将逻辑封装在类的内部,对外除提供的public方法,不对外泄漏任何信息。迪米特法则还有1个更简单的定义:只与直接的朋友通讯。首先来解释1下甚么是直接的朋友:每一个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的情势出现在类的内部。
1句话总结就是:1个对象应当对其他对象保持最少的了解。
7、单1职责原则(Single Responsibility Principle)
定义:不要存在多于1个致使类变更的缘由。通俗的说,即1个类只负责1项职责,应当唯一1个引发它变化的缘由
说到单1职责原则,很多人都会嗤之以鼻。由于它太简单了。稍有经验的程序员即便历来没有读过设计模式、历来没有听说过单1职责原则,在设计软件时也会自觉的遵照这1重要原则,由于这是常识。在软件编程中,谁也不希望由于修改了1个功能致使其他的功能产生故障。而避免出现这1问题的方法便是遵守单1职责原则。虽然单1职责原则如此简单,并且被认为是常识,但是即使是经验丰富的程序员写出的程序,也会有背背这1原则的代码存在。为何会出现这类现象呢?由于有职责分散。所谓职责分散,就是由于某种缘由,职责P被分化为粒度更细的职责P1和P2。
遵守单1职责原的优点有:
1.可以下降类的复杂度,1个类只负责1项职责,其逻辑肯定要比负责多项职责简单的多;
2.提高类的可读性,提高系统的可保护性;
3.变更引发的风险下降,变更是必定的,如果单1职责原则遵照的好,当修改1个功能时,可以显著下降对其他功能的影响。
需要说明的1点是单1职责原则不只是面向对象编程思想所独有的,只要是模块化的程序设计,都需要遵守这1重要原则。
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠