《设计模式之禅》之六大设计原则上篇

本文主要讲单一职责原则和里氏替换原则。
sql

1、单一职责原则

1.定义

应该有且有一个缘由引发类的变动。架构

2.单一职责原则的好处

好处以下:

  • 类的复杂性下降,实现什么职责都有明确清晰的定义;
  • 可读性提升,复杂性下降,那固然可读性提升;
  • 可维护性提升,可读性提升,那固然更容易维护了;
  • 变动引发的风险下降,变动是没必要可少的,若是接口的单一职责作得好,一个接口修改只对相应的实现类有影响,对其余的接口无影响,这对系统的扩展性、维护性都有很是大的帮助;

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

3.小结

对于单一职责原则,以我实际开发为例,在公司开发项目,基本上沿用得开发模式就是MVC,模型-视图-控制器,每一个职责不同。而后再往大的范围来讲,分层,好比数据访问层、业务逻辑层、视图表现层(其中该层就是MVC的应用)。
数据访问层,一般编写的就是一些基础的CRUD,而业务逻辑层就是数据访问层里的CRUD一同用于处理某项业务。jsp

还有就是单一职责的一个体现就是一个函数办一件事情,比方说业务逻辑层中修改密码(设计db操做修改用户信息),最好是修改密码是一个方法,修改用户基本信息(例如昵称、性别、职位、籍贯等)是另一个方法。不一样的方法(不一样的函数)办的事情不同,我以为这样也是单一职责的一个最好实践。记得刚工做第一年的时候,写代码基本上数据访问层就和业务逻辑层是同样的,这样的写法致使的后果是,若是是少许的五到六个类还好,但若是数十个的话或成百上千个这样写的话,后果将会很是严重,直接会致使维护成本的上升,可扩展性差、可维护性差等。函数

最后概括一点,单一职责若是是以本身平时写写Demo玩玩而言实现起来并不困难。可是对于公司而言就不同了,拿我曾经接手的一个项目来讲,该项目采用jfinal框架,而后前任架构师对其又再度修改封装了不少东西,能够称之为扩展。当我接手这个项目的时候,初看项目结构,捋了下,大体能根据包名看出功能分层,但当我深刻阅读的时候,发现太多的拼接sql,并且业务逻辑层和数据访问层并未分层明确,就像咱们通常开发喜欢分为视图、业务、数据这样的分层,而这个项目基本上就是视图(一种是返回jsp,另一种做为路由返回JSON数据),业务实现(基本上就是写业务逻辑的),还有较多的dto、request、response等包名下的众多类。spa

一般我所认为的最佳实践就是:
1.分层(表现层-业务逻辑层-数据访问层,每层作的事情不同);
2.一个函数只办一件事情(也许你会发现你可能会编写比较多的函数,例如用户修改密码,通常能够修改用户信息就能实现,可是通常建议分为两个,由于这样一来当数据表结构发生变化,例如新增或修改字段,会下降不少影响,因此不要为了偷懒就用一个函数办两件或多件事情);设计

对于单一职责原则,接口必定要作到单一职责,类的设计尽可能作到只有一个缘由引发变化。对象

2、里氏替换原则

继承的优势:继承

  • 代码共享,减小建立类的工做量,每一个子类都拥有父类的方法和属性;
  • 提升代码的重用性;
  • 子类能够形似父类,但又异于父类,例如”龙生龙,风生风,老鼠生来会打洞”,说的就是子拥有父的”种”;
  • “子”是指明子与父的不一样;
  • 提升代码的可扩展性,实现父类的方法就能够”随心所欲”了,君不见不少开源框架的扩张接口都是经过继承父类来完成的;
  • 提升产品或项目的开放性;

继承的缺点:接口

  • 继承是侵入性的。只要继承,就必须拥有父类的全部属性和方法;
  • 下降代码的灵活性。子类必需要拥有父类的属性和方法,让子类自由的世界中多了些约束;
  • 加强耦合性。当父类的常量、变量和方法被修改时,须要考虑子类的修改,并且在缺少规范的环境下,这种修改可能带来很是糟糕结果-一大段的代码须要重构;

1.里氏替换原则定义

那么什么是里氏替换原则呢?
全部引用基类的地方必须能透明地使用其子类的对象。

通俗的来讲:只要父类能出现的地方子类就能够出现,并且替换为子类也不会产生任何错误或异常,使用者可能根本就不须要知道是父类仍是子类。可是,反过来就不行了,有子类出现的地方,父类未必适应。

2.规范

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

(1)子类必须彻底实现父类的方法

注意:
a.在类中调用其余类时务必要使用父类或接口,若是不能使用父类或接口,则说明类的设计已经违背了LSP原则;
b.若是子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生”畸变”,则建议断开父子继承关系,采用依赖、汇集、组合等关系代替继承。

(2)子类能够有本身的个性

子类能够有本身的行为和外观,也就是方法与属性,那这里为何要再提呢?
是由于里氏替换原则能够正着用,可是不能反着用。在子类出现的地方,父类未必能胜任。

(3)覆盖或实现父类的方法时输入参数能够被放大

方法中的输入参数称为前置条件,这是什么意思?
WebService有一个”契约优先”原则,也就是先定义出WSDL接口,制定好双方的开发协议,而后再各自实现。里氏替换原则也要求制定一个契约,就是父类或接口,这种设计方法也叫作契约设计,与里氏替换原则有着殊途同归之妙。契约制定了,也就同时制定了前置条件和后置条件,前置条件就是你要让我执行,就必须知足个人条件;后置条件就是我执行完了须要反馈,标准是什么。

(4)覆写或实现父类的方法时输出结果能够被缩小

这是什么意思呢?
父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一个类型,要么S是T的子类,为何呢?
分两种状况:
若是是覆写,父类和子类的同名方法的输入参数是相同的,两个方法的范围值小于等于T,这是覆写的要求,这才是重中之重,子类覆写父类的方法,天经地义。
若是是重载,则要求方法的输入参数类型或数量不相同,在里氏替换原则要求下,就是子类的输入参数宽于或等于父类的输入参数,也就是说你写的这个方法是不会被调用的,参考上面讲的前置条件。

采用里氏替换原则的目的就是加强程序的健壮性,版本升级时也能够保持很是好的兼容性。即便增长子类,原有的子类还能够继续运行。在实际项目中,每一个子类对应不一样的业务含义,使用父类做为参数,传递不一样的业务含义,使用父类做为参数,传递不一样的子类完成不一样的业务逻辑。

3.小结

在项目中,采用里氏替换原则时,尽可能避免子类的”个性”,一旦子类有”个性”,这个子类和父类之间的关系就很难调和了,把子类当作父类使用,子类的”个性”被抹杀;把子类单独做为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离,缺少类替换的标准。

相关文章
相关标签/搜索