设计模式-六大设计原则

原创播客,如需转载请注明出处。原文地址:https://www.cnblogs.com/ThreeDonkey/p/10231092.html 

----------------------------------------------------------------------------------------------------------------------------------------------------------

笔记中提供了必要的代码示例,需要说明的是,大部分代码示例都是本人所敲代码并进行测试,不足之处,请大家指正~

本博客中所有言论仅代表博主本人观点,若有疑惑或想要与我一块成长,敬请联系 [email protected]

-----------------------------------------------------------------------------------------------------------------------------------------------------------

一、序

  前一阵从csdn开通了个人博客,由于个人喜好后来搬家到博客园。然后中间有很大一段时间没有更新新的博客,一是发生了许多事情没有时间来写博客,二是也没有认真深入的搞一些东西。最近又要回来了,哈哈。还是比较开心的。我当时写博客的初衷呢,一是为了让所学到的东西达到一个闭环,让所学东西有一个输入到输出的过程(某些东西没法接着应用起来,通过这种输出方式可以让所学达到一个闭环);二是自己也是这种“开源”开放平台的受益者,看了不知道多少前辈所写的博客,收益很多,然后尽量贡献自己的一份力量,让别人也能从自己这微薄之力中收益一下(哈哈,感觉很有成就感,不要脸.jpg);三是通过写博客可以与别人交流,从而发现自己的问题以及不足(前提别人看了,并且与你交流了,哈哈我相信随着时间的推移,总会在园子里认识很多好朋友的)以改善提高自己。

二、前言

  之前粗略的了解过设计模式,最近又重新来了一遍。

  个人观点:设计模式真的不是生搬硬套的,前提你要有很大很大的代码量,然后碰到过很多的的需求,也要改过很多很多的需求,你才能真正把设计模式灵活的用起来,并感觉到设计模式的强大与魅力所在。

  然后不谋而合的是最近读了码农翻身公众号里冰果老师的文章,然后我就引用下冰果老师对设计模式的观点:

  如果没有一定的实际开发经验与代码编写量,或者为了设计模式而设计模式,那么建议还是暂时不要管它。建议从最简单的KISS原则开始,或者从一定的代码量后,追求可读性追求更高目标,可以先做一系列重命名提取方法等基础重构。之后如果仍然觉得代码依然缺乏优雅的特性,再考虑重构到设计模式。总而言之,设计模式不应该生搬硬套塞进代码里,而是应该根据需要,从代码中自然生长出来。

  冰果老师最后还给出了建议:

  总而言之,对于缺乏实际编码经验,或者代码量写得太少的童鞋们,建议是先不要碰设计模式了。如果为了应付面试,或者想先大致了解以方便吹牛,那么就从简单的模式开始吧,首先理解其意图任然是最重要的事情。

  本菜鸡还是本着自己写博客的初衷,然后这次写设计模式方面的东西呢,主要是学习设计模式的思想,嗯,就这样,以后总再自己的代码里用到的。

三、声明

  本菜鸡所学设计模式的主要阅读了两本书,一是自己购买的秦晓波老师的《设计模式之禅》(此书是通俗易懂,案例简介明了);二是学校所发的耿祥义、张跃平老师的《面向对象与设计模式》(此书不太怎么讲模式,几乎直接每个章节配两个该模式思想的编写的代码例子)。博客中可能会出现这两本书的大量代码,原书两位老师的原话、思想,以及个人理解融汇贯通,力求做到清晰明了,哈哈 废话不多说,开始。

四、六大设计原则

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

    单一职责原则的定义是:应该有且仅有一个原因引起类的变化。

    一个职责一个接口,但问题是“职责”没有一个量化的标准,一个类到底要负责那些职责?这些职责怎么去细化?这些都要从实际项目出发去考虑。

    注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量借口或类设计的是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。

    优点: 

  •       类的复杂性降低,实现什么职责都有清晰明确的定义;
  •       可读性提高,复杂性提高,那当然可读性提高了;
  •       可维护性提高了,可读性提高了,那当然更容易维护了;
  •       变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他接口无影响,这对系统的拓展、维护性都有非常大的帮助。

    建议:接口一定要做到单一职责,类的设计尽量要做到只有一个原因引起变化。

---恢复内容结束---

一、序

  前一阵从csdn开通了个人博客,由于个人喜好后来搬家到博客园。然后中间有很大一段时间没有更新新的博客,一是发生了许多事情没有时间来写博客,二是也没有认真深入的搞一些东西。最近又要回来了,哈哈。还是比较开心的。我当时写博客的初衷呢,一是为了让所学到的东西达到一个闭环,让所学东西有一个输入到输出的过程(某些东西没法接着应用起来,通过这种输出方式可以让所学达到一个闭环);二是自己也是这种“开源”开放平台的受益者,看了不知道多少前辈所写的博客,收益很多,然后尽量贡献自己的一份力量,让别人也能从自己这微薄之力中收益一下(哈哈,感觉很有成就感,不要脸.jpg);三是通过写博客可以与别人交流,从而发现自己的问题以及不足(前提别人看了,并且与你交流了,哈哈我相信随着时间的推移,总会在园子里认识很多好朋友的)以改善提高自己。

二、前言

  之前粗略的了解过设计模式,最近又重新来了一遍。

  个人观点:设计模式真的不是生搬硬套的,前提你要有很大很大的代码量,然后碰到过很多的的需求,也要改过很多很多的需求,你才能真正把设计模式灵活的用起来,并感觉到设计模式的强大与魅力所在。

  然后不谋而合的是最近读了码农翻身公众号里冰果老师的文章,然后我就引用下冰果老师对设计模式的观点:

  如果没有一定的实际开发经验与代码编写量,或者为了设计模式而设计模式,那么建议还是暂时不要管它。建议从最简单的KISS原则开始,或者从一定的代码量后,追求可读性追求更高目标,可以先做一系列重命名提取方法等基础重构。之后如果仍然觉得代码依然缺乏优雅的特性,再考虑重构到设计模式。总而言之,设计模式不应该生搬硬套塞进代码里,而是应该根据需要,从代码中自然生长出来。

  冰果老师最后还给出了建议:

  总而言之,对于缺乏实际编码经验,或者代码量写得太少的童鞋们,建议是先不要碰设计模式了。如果为了应付面试,或者想先大致了解以方便吹牛,那么就从简单的模式开始吧,首先理解其意图任然是最重要的事情。

  本菜鸡还是本着自己写博客的初衷,然后这次写设计模式方面的东西呢,主要是学习设计模式的思想,嗯,就这样,以后总在自己的代码里用到的。

三、声明

  本菜鸡所学设计模式的主要阅读了两本书,一是自己购买的秦晓波老师的《设计模式之禅》(此书是通俗易懂,案例简介明了);二是学校所发的耿祥义、张跃平老师的《面向对象与设计模式》(此书不太怎么讲模式,几乎直接每个章节配两个该模式思想的编写的代码例子)。博客中可能会出现这两本书的大量代码,原书两位老师的原话、思想,以及个人理解融汇贯通,力求做到清晰明了,哈哈 废话不多说,开始。

四、六大设计原则

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

    单一职责原则的定义:应该有且仅有一个原因引起类的变化。

    一个职责一个接口,但问题是“职责”没有一个量化的标准,一个类到底要负责那些职责?这些职责怎么去细化?这些都要从实际项目出发去考虑。

    注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量借口或类设计的是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。

    优点: 

  •       类的复杂性降低,实现什么职责都有清晰明确的定义;
  •       可读性提高,复杂性提高,那当然可读性提高了;
  •       可维护性提高了,可读性提高了,那当然更容易维护了;
  •       变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他接口无影响,这对系统的拓展、维护性都有非常大的帮助。

    建议:接口一定要做到单一职责,类的设计尽量要做到只有一个原因引起变化。

  (2)里氏替换原则(Liskov Substitution Principle,简称LSP)

    里氏替换原则的定义是(第一种):如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有所有程序P在所有对象o1都替换为o2时,程序P的行为没有变化,那么类型S是类型T的子类型。

    里氏替换原则的定义是(第二种): 所有引用基类型的地方必须能够透明的使用其子类的对象。

    也就是说,只要父类能够出现的地方子类就能出现,而且替换为子类也不能产生任何错误或者异常,使用者可能根本就不需要这倒是父类还是子类(此句为重点把握这一句即可理解历史替换原则宗旨)。但是反过来就不行,有子类出现的地方,父类未必就能适应。

    里氏替换原则为良好的继承定义了一个规范,一句简单的定义包含了四层含义。

      1、子类必须完全实现父类的方法。

        我们在做系统设计时,经常会定义一个接口或者抽象类,然后编码实现,调用类则直接传入接口或者抽象类,其实这里已经使用了里氏替换原则。

        注意:(如果子类不能完全的实现父类的方法,或者父类的某些方法在子类中已经发生了“畸变”,则建议断开父子继承关系,采用依赖,聚合,组合关系来代替继承。)

      2、子类可以有自己的个性。

        当子类有了自己的属性和方法时,有子类出现的地方就不能用父类替换了。里氏替换原则可以正着用,但是不能翻过来用。在子类出现的地方,父类未必就可以胜任。

      3、覆盖或实现父类的方法时输入参数可以被放大。

        若子类继承的父类后某个方法的传入参数的类型覆盖范围宽于父类了,请注意这不是重写的父类方法,而是在继承了父类的方法后对该方法进行了重载。如此一来,你再用子类去替代父类出现的地方,执行的方法就是原父类的方法。

      4、覆写或实现父类的方法时输出结果可以缩小。

        当一个方法覆写或重载父某个方法后,要求的返回值的覆盖范围要小于等于父类,这样在用到参数的地方才能符合里氏替换原则。

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

    依赖倒置原则的定义:高层模块不应该依赖底层模块,两者都因该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;

    依赖倒置原则再Java语言中的表现就是:

       1、模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖是通过接口或者抽象类产生的;

       2、抽象类或者接口不依赖与实现类;

       3、实现类依赖接口或者抽象类;

     总结:精髓所在就是面向接口编程。

  (4)接口隔离原则(Interface Segregation Principle)

    接口隔离原则的定义(第一种):客户端不应该依赖他不需要的接口。

    接口隔离原则的定义(第二种):类间的依赖关系应该建立在最小的接口上。

      第一种定义:“客户端不应该他不需要的接口”,那依赖什么,依赖他需要的接口,客户端需要什么接口就提供什么接口,把不需要的接口剔除,对接口进行细化,保证接口的纯洁性。;

      第二种定定义:“类间的依赖关系应该建立在最小的接口上”他要求也是最小的接口,接口细化,接口纯洁;

      我们把两个定义概括为一句话:建立单一接口,不要建立臃肿庞大的接口。接口尽量细化,接口中的方法尽量少。

      注意:(接口隔离原则与单一职责原则的审视视角是不同的,单一职责要求的类和接口职责单一,注重的是职责,这是业务上的划分,而接口隔离原则的要求是接口中的方法尽量少。)

    接口隔离原则是对接口进行规范约束,其包含一下4层含义:

      1、接口尽量小  

        接口尽量要小,但是不要违背单一职责原则(根据接口隔离原则拆分接口时,首先必须满足单一职责原则)。

      2、接口要高内聚

        提高接口、类、模块的处理能力,减少对外的交互。

      3、定制服务

        一个系统或者系统内的模块之间必然会有耦合,有耦合就要有互相访问的接口我们设计时就需要为各个访问者(即客户端)定制服务,什么是定制服务?就是只提供访问者需要的方法。

      4、接口设计是有限度的

        接口设计的粒度越小,系统就越灵活,但是灵活的同时也带来结构的复杂化,开发难度增加,所以接口的设计一定要注意适度,这个度通常要根据经验来判断。

  (5)迪米特法则(Law of Demeter,LOD)也成为最少知识原则(Least Knowledge Principle,LKP)

    一个对象应该对其他对象有最最少的了解。一个类应该对自己耦合的类知道的最少,你内部是如何复杂的实现与我无关,我只要知道你提供的public方法。

    迪米特法则对类间的低耦合提出了明确的要求,其包含以下四层含义。

    迪米特法则的核心概念就是类间解耦,弱耦合,只有弱耦合了以后,累的复用率才能提高。

   (6)开闭原则(Open Closed Principle)

     开闭原则定义:一个软件实体如类、模块和函数应该对拓展开放,对修改关闭。

     开闭原则已经非常明确的告诉我们:软件实体应该对拓展开放,对修改关闭,其含义是说一个软件实体应该通过拓展来实现变化,而不是通过修改已有的代码来实现变化。

      开闭原则的重要性:

        1.开闭原则对测试的影响:所有投产的代码都是有意义的,并且接受系统规范的约束,而且都是经过了“千锤百炼”的测试,保证了起正确性以及健壮性。如果我们要修改了原有代码,则要重新对原有测试流程再进行一遍。如果我们通过拓展,而不是修改来应对变化,只需要保证新添加的代码的测试正确就行。

        2.开闭原则可以提高复用性:所有的逻辑都是从原子的逻辑组合而来的的,只有粒度越小,被复用的可能性就越大。

        3.开闭原则可以提高可维护性:投产后的软件,要进行拓展是比较容易的,要进行修改是复杂的。

        4.面向对象开发的要求:万物皆对象,万物皆运动,有运动就有变化,如何能够快速应对变化,就要能够快速去拓展应对变化,要比修改应对变化更容易。

      如何使用开闭原则:

        1.抽象约束:

          抽象对一组事物的通用描述,没有具体实现,也就表示他可以有非常多的可能性,可以跟随需求变化而变化。

           (1).通过接口或者抽象类约束拓展,对拓展进行边界限定,不允许出现在接口或者抽象类中不存在的public方法

           (2).参数类型、引用对象尽量使用接口或者抽象类而不是实现类。

           (3).抽象尽量保持稳定,一旦确定即不允许修改。

        2.元数据(metadata)控制模块行为

          尽量通过元数据来控制程序的行为。什么是元数据?用来描述环境和数据的数据,通俗的说也就是配置参数,参数可以从文件中获得,也可以从数据库中获得。

        3.制定项目章程

          在团队中建立章程,因为章程中指定了所有开发人员都必须遵守的约定,对项目来说,约定优于配置。 

        4.封装变化

          将相同的变化封装到一个接口或者抽象类中,将不同的变化封装到不同的接口或者抽象类中。

      开闭原则是重中之重,是最基础的原则,是其他五大原则的精神领袖,开闭原则只是个规则,拥抱变化,并不局限于这六大原则,但在项目中应该尽量使用者六大原则。开闭原则只是一个终极目标,任何人都无法百分之百做到,但朝着这个方向努力,可以非常显著地改善一个系统架构,做到“拥抱变化”。

猜你喜欢

转载自www.cnblogs.com/ThreeDonkey/p/10231092.html