10张图说清设计模式6大原则

Posted Sheldon的编程笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10张图说清设计模式6大原则相关的知识,希望对你有一定的参考价值。

设计模式的六大原则



1. 单一职责原则


  • 核心思想:任何一个软件模块中,应该有且只有一个被修改的原因。

  • 栗子描述:Java程序猿非常喜欢把各种杂七杂八的功能性函数放到一个CommonService类里面,打 log 啊,各种查询啊,鉴权啊。你要修改这个类的时候原因是不是很多?log 样式要改,查询条件要改,鉴权要加等等。咳咳,这就违反了单一职责原则啦。


怎么修改呢? 就是一个类只做好一类事情啊,如下图。这样你改任何一个类就只有一个理由了。比如你要改日志类,理由就是你要修改日志相关的功能。


10张图说清设计模式6大原则


总结一下单一职责原则:

  • 优点:职责越单一,被修改的原因就越少,类的复杂度降低、可读性提高、可维护性提高、扩展性提高、降低了变更引起的风险。

  • 缺点:如果类一味追求单一职责,有时会造成类的大爆炸。。。。。。。不过接口和方法肯定要遵循这个原则。

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



2. 依赖倒置原则


  • 核心思想:高层模块不应该依赖底层模块,二者都该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;

  • 说明:高层模块就是调用端,低层模块就是具体实现类。抽象就是指接口或抽象类。细节就是实现类。

  • 通俗来讲:依赖倒置原则的本质就是通过抽象(接口或抽象类)使个各类或模块的实现彼此独立,互不影响,实现模块间的松耦合。模块之间交互应该依赖抽象,而非实现。

  • 优点:大幅提高了可扩展性,降低耦合度,使代码层次更加清晰。


client 直接调用具体实现类。模块之间交互应该依赖抽象,而非实现。这里违背了依赖倒置原则,但在业务最初的时候不会有任何问题。


10张图说清设计模式6大原则

随着业务的增加,需要加上非关系型数据库NoSQL,还对接了些政府业务,对方用Oracle。


10张图说清设计模式6大原则


这样做的问题很明显,应用代码强耦合了实现类,当需要切换新的数据源实现的话,就不得不扒开原来的应用代码做修改了?出了bug是不是要背锅了?Leader可能要给你送飞机票了。


如果按照依赖倒置原则呢? 定义接口,让不同的实现类去实现这个接口。我们发现通过接口,实现了依赖倒置。业务代码不再依赖实现类,而是依赖接口,而同时,实现类也依赖接口。模块之间交互从依赖实现到依赖抽象。


10张图说清设计模式6大原则

如果要切换数据库,改一行代码直接搞定不香吗?


DbService dbService = new mysqlService(); //改成NoSqlService, OracleService即可



还比较迷茫的童鞋可以移步《》



3. 开闭原则


  • 核心思想:软件实体应该「扩展开放,对修改关闭」。

  • 说明:对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展以适应新的情况。对修改关闭,意味着类的设计一旦完成,就可以独立完成工作,而不要对其进行任何修改。


还是上文数据库接口的例子,当你需要新增数据库时,原来的MySQL实现类无需改动,直接新增Oracle实现类和NoSQL实现类即可。也就是「对扩展开放,对修改关闭」。

10张图说清设计模式6大原则


4.接口隔离原则


  • 核心思想:多个特定的接口要好于一个宽泛用途的接口

  • 通俗来讲:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。


类A通过接口interface依赖类B,类C通过接口interface依赖类D,如果接口interface对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。


10张图说清设计模式6大原则


我们按照ISP原则改上一版。比如A不需要用到方法4,方法5。就可以选择不依赖他们。


10张图说清设计模式6大原则


总结一下接口隔离原则:

  • 优点:将外部依赖减到最少。你只需要依赖你需要的东西。这样可以降低模块之间的耦合。

  • 注意:

    • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。

    • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事



5. 迪米特法则(Law of Demeter)


  • 核心思想:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立


法则强调了以下两点:

  • 第一: 从被依赖者的角度来说:只暴露应该暴露的方法或者属性

  • 第二: 从依赖者的角度来说:只依赖应该依赖的对象


针对第一点,举个栗子。当我们对计算机进行关机的时候,会先执行一些列的动作:比如保存当前未完成的任务,然后是关闭相关的服务,接着是关闭显示器,最后是关闭电源,这一系列的操作以此完成后,计算机才会正式被关闭。 


如下代码违背了迪米特原则(只暴露应该暴露的方法或者属性)。我们看到close()方法已经封装好了所有关机流程,其他方法没有必要是public。只有close()方法才可以被其他调用computer的类可见。


10张图说清设计模式6大原则


针对第二点,举个栗子。Sheldon和学霸Wang是好基友。某天Sheldon想认识个妹子,而学霸Wang和妹子认识。Sheldon如果直接去撩妹子,妹子会认为你是个渣男直接拒绝。所以Sheldon只能通过学霸Wang,由学霸Wang去传递信息给妹子(只依赖应该依赖的对象)。




6. 里氏替换原则


  • 核心思想:程序中的父类型都应该可以正确地被子类替换。

  • 原理:LSP 是继承复用的基石,只有当子类可以替换掉父类,且软件的功能不受到任何影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。

  • 通俗来讲:只要有父类出现的地方,都可以使用子类来替代。而且不会出现任何错误或者异常。但是反过来却不行。子类出现的地方,不能使用父类来替代。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。

  • 优点:代码共享,减少创建类的工作量。提高代码的重用性,可扩展性。

  • 缺点:继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改

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

  • 最佳实践:我们最好将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时候,父类不能实例化



总结



  • 单一职责原则:提高代码实现层的内聚度,降低实现单元彼此之间的耦合度

  • 开闭原则:提高代码实现层的可扩展性,提高面临改变的可适应性,降低修改代码的冗余度

  • 里氏替换原则:提高代码抽象层的可维护性,提高实现层代码与抽象层的一致性

  • 接口隔离原则:提高代码抽象层的内聚度,降低代码实现层与抽象层的耦合度,降低代码实现层的冗余度

  • 依赖倒置原则:降低代码实现层由依赖关系产生的耦合度,提高代码实现层的可测试性

  • 迪米特法则:一个对象应该对其他对象保持最少的了解。尽量降低类与类之间的耦合。

借用一张图,希望大家从真正意义上做到将这些原则牢记于心,并付诸于行。(来源:https://segmentfault.com/a/1190000013208730)

原则 耦合度 内聚度 扩展性 冗余度 维护性 测试性 适应性 一致性
单一职责原则 - + o o + + o o
开闭原则 o o + - + o + o
里氏替换原则 - o o o + o o +
接口隔离原则 - + o - o o + o
依赖倒置原则 - o o - o + + o

Note: +代表增加, -代表降低, o代表持平




以上是关于10张图说清设计模式6大原则的主要内容,如果未能解决你的问题,请参考以下文章

大牛用1万字30张图说清TCP协议

设计模式6大原则

设计模式6大原则

JAVA设计模式的6大原则

设计模式6大原则

设计模式--6大原则--开闭原则