设计模式中的原则

单一职责原则

就一个类而言,应该仅有一个引起它变化的原因.

A: 开发俄罗斯方块的小游戏,如何考虑?
B: 分类?这里好像关键在于各种事件代码如何写吧,这里有什么类可言呢?
A: 现在的代码不能复用.但是这当中,有些东西始终没变.
B: 下落,旋转等这些游戏逻辑?
A: 这些都是游戏有关的逻辑,和界面如何表示没有关系,为什么要写在同一个类里面呢?如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或抑制这个类完成其他职责的能力.这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏.
A: 所谓游戏逻辑,不过就是数组的每一项值变化的问题,下落旋转等这些都是在数组具体项的值的变化.而界面表示逻辑,不过是根据数组的数据进行绘出和擦除,或者根据键盘命令调整数组的相应方法进行改变.至少分为2类.
A: 当然,软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离.其实要去判断是否应该分离出了来,也不难,那就是如果你能够想到多一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑职责分离.

开放-封闭原则

是说软件实体(类,模块,函数等等)应该可以扩展,但是不可修改(原来的代码).

A: 有2个特征:1)对于扩展是开放的;2)对于更改是封闭的.
A: 需求确定,就再也不会变化,这是不现实的.怎么的设计才能面对需求的改变却可以保存稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢?开发-封闭原则给了我们答案.
A: 比如说打卡问题.迟到不是主要问题,每天保证8小时的工作量是老板最需要的,最最重要的是业绩目标的完成或超额完成才是最重要的指标,于是改变管理方式.这其实就是对于工作时间或业绩成效的修改关闭,而对时间制度扩展的开放.

何时应对变化

  • 在设计的时候,时刻要考虑如何让这个类足够好.
  • 绝对的对修改关闭是不可能的.无论模块是多么的’封闭’,都会存在一些无法对之封闭的变化.既然不可能完全封闭,设计人员必须对于他设计的模块应该对那种变化封闭做出选择.他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化.
  • 我们是很难猜对,但我们却可以在发生小变化时,就及早去想办法应对发生更大变化的可能.也就是说,等到变化发生时立即采取行动.
  • 在我们最初编写代码时,假设变化不会发生.但变化发生时,我们就创建抽象来隔离以后发生的同类变化.例如加法程序中,增加功能需要修改原来的这个类,这就违背了今天讲的开放-封闭原则,于是你就应该重构程序,增加一个抽象的运算类,通过一些面向对象的手段.这时我又要增加功能,增加子类就可以了.即面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码.
  • 我们希望的是在开放工作开展不久就知道可能发生的变化.查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难.
  • 开放-封闭原则是面向对象设计的核心所在.遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护,可扩展,可复用,灵活性好.开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意.拒绝不成熟的抽象和抽象本身一样重要.

依赖倒转原则

高层模块不应该依赖底层模块.两个都应该依赖抽象.抽象不应该依赖细节.细节应该依赖抽象.

  • 单一职责原则: 就刚才修电脑的事,显然内存坏了,不应该成为更换CPU的理由,它们各自的职责是明确的.
  • 开放-封闭原则: 内存不够只要插槽只够就可以添加,硬盘不够可以用移动硬盘等.
  • 依赖倒转原则: 抽象不应该依赖细节,细节应该依赖抽象.说白了,就是要针对接口编程,不要针对实现编程.

为什么要叫倒转

面向对象的开发过程时,为了使得常用的代码可以复用,一般都会把这些常用的代码写成许许多多的程序库,这样我们在做新项目时,去调用这些底层的函数就可以了.这也就叫做高层模块依赖低层模块.

问题就出现在这里,我们要做新项目时,发现业务逻辑的高层模块都是一样的,但是客户却希望使用不同的数据库或存储信息,这时就出现麻烦了.我们希望能再次利用这些高层模块,但是高层模块都是与低层的访问数据库绑定在一起的,没有办法复用这些高层模块.
而如果不管高层模块还是底层模块,它们都依赖于抽象,具体一点就是接口或抽象类,只要接口是稳定的,那么任何一个的更改都不用当心其他受到影响.

里氏代换原则

子类必须能替换掉它们的父类型

白话翻译:

一个软件实体如果使用的是一个父类的话,那么一定适用其子类而且它觉察不出父类对象和子类对象的区别.也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化.

A: 企鹅能继承鸟类吗?
B: 企鹅是一种鸟,当然可以继承.
A: 你上当了,在编程世界里,鸟都能飞,但是企鹅不能飞.企鹅不能以父类出现–鸟的身份出现,因为前提说所有的鸟都能飞,而企鹅飞不了,所以,企鹅不能继承鸟类.
A: 只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为.
B: 由于子类的可替换性才使得父类类型的模块在无需修改的情况下就可以扩展.在回过头来看依赖倒转原则,高层模块不应该依赖低层模块,两个都应该依赖抽象.

比如:修收音机

依赖倒转其实可以说是面向对象设计的标志,用那种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了.

迪米特法则

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

A: 首先强调的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限。迪米特法则其根本思想,是强调了类之间的耦合。你第一天去公司,怎么会认识IT部的人呢,如果公司有很好的管理,那么应该是人事的小杨打个电话给IT部,告诉主管安排人给小菜你装电脑,就算开始是小张负责,他临时有事,主管也可以在安排小李来处理。同样的道理,我们在程序设计时,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。

无熟人难办事

B: 当然是问IT部比问具体的人来的更好。
A: 无论认不认识人,他们都会想办法找人来解决。
B: IT部代表是抽象类或接口,小张小李代表是具体类,之前你在分析会修电脑不会修收音机里讲的依赖倒转原则,即面向对象编程,不要面向实现编程就是这个意思?

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000013481331
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞