设计模式之6个基本原则

六个基本原则:
---单一职责原则  Single Responsibility Principle
---开闭原则      Open Closed Principle
---里氏替换原则  Liskov Substitution Principle
---迪米特法则    Law of Demeter
---接口隔离原则  Interface Segregation Principle
---依赖倒置原则  Dependence Inversion Priciple


1.单一职责原则  Single Responsibility Principle   

---应该有且只有一个原因引起类的变更  (SRP)

---There should never be more than one reason for a class to change.


优点:
---类的复杂性降低,实现什么职责都有清晰明确的定义
---可读性提高
---可维护性提高
---变更引起的风险降低

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

----里氏替换原则是针对继承的扬长弊端的有效工具。

继承的优点:

---代码共享
---代码重用
---子类与父类形似
---提高可扩展性
---开放性
继承的缺点:
---代码是侵入式的
---降低代码的灵活性
----增加了耦合性。当父类的代码被修改,必须考虑子类的修改
继承的扬长避短神器:里氏替换原则
目的:增强程序的健壮性,版本升级时也可以保持非常好的兼容性。


里氏替换定义方式一:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

里氏替换定义方式二:Functions that use pointers or references to base classed must be able to use objects of derived classes without knowing it.

应用规则:
(1)子类必须完全实现父类的方法
     如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚合、组合等关系代替继承。
(2)子类可以有自己的个性
     里氏替换原则可以正着用,但不能反过来。
(3)覆盖或实现父类的方法时输入参数可以被放大
     子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更加宽松。
(4)覆盖或实现父类的方法时输出结果可以被缩小
     父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一个类型,要么S是T的子类。
     
3.依赖倒置原则(DIP,Dependence Inversion Principle)
定义:High level modules should not depend upon low level modules.Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.
翻译过来包括三层含义:
-》高层模块不应该依赖低层模块,两者都应该依赖抽象。
-》抽象不应该依赖细节
-》细节应该依赖抽象
(注:抽象就是接口和抽象类,细节是指实现类)

另一种表发方式是:
-》模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系时通过接口或抽象类产生的。
-》接口或抽象类不依赖于实现类
-》实现类依赖接口或抽象类

更加精简的定义就是:面向接口编程

依赖关系的三种方法:
-》构造函数传递依赖对象--构造注入
-》Setter方法传递依赖对象--setter注入
-》接口声明依赖对象

最佳实践:
-》每个类都尽量有接口或抽象类,或者接口与抽象类两者都具备
-》变量的表面类型尽量是接口或者抽象类 
      **很多书上说变量的类型一定要是接口或者抽象类,这个优点绝对化了,比如一个工具类,xxxUtils一般不需要接口或者抽象类的,还有,如果你要试用类的clone方法,就必须试用实现类,这个是JDK提供的一个规范。
-》任何类都不应该从具体类派生
   **尽量不派生,但有时难度大,因此不超过两层是可以忍受的。
-》尽量不要覆盖基类的方法
    **如果基类是一个抽象类,而且已经实现了,子类尽量不要覆写。类间依赖的是抽象,如果覆写了抽象方法,对依赖的稳定性会产生一定的影响。
-》结合里氏替换原则使用
    里氏替换:父类出现的地方,子类就可以出现。


4.接口隔离原则(Interface Segregation Priciple)
  隔离的含义:客户端不应该依赖它不需要的接口;类间的依赖关系应该建立在最小的接口上。
  >>>建立单一接口,不要建立臃肿庞大的接口。接口尽量细化,接口中的方法尽量少。
    **************************************************这与单一职责原则不是相同的吗?错。接口隔离原则与单一职责的审视角度是不同的,单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑的划分,而接口隔离原则要求接口的方法尽量少。例如一个接口的职责可能包含10个方法,者10个方法都放在一个接口中,并且提供给多个模块访问,各个模块按照规定的权限来访问,在系统外通过文档约束“不适用的方法不要访问”,按照单一职责原则是允许的,按照接口隔离原则是不允许的,因为它要求“尽量使用多个专门的接口”。专门的接口是指什么?就是只提供给每个模块的都应该是单一接口,提供给几个模块就应该有几个接口,而不是建立一个庞大的臃肿的接口,容纳所有的客户端访问。******************************************************
 
接口隔离原则包含以下4层含义:
(1)接口要尽量小
     **但是,不能违反单一职责原则
(2)接口要高内聚
(3)定制服务
(4)接口设计是有限度的

根据以下规则衡量:
(1)一个接口只服务于一个子模块或业务逻辑
(2)通过业务逻辑压缩接口中的public方法
  (3) 已经被污染了的接口,尽量去修改
(4)了解环境,拒绝盲从

5、迪米特法则(Law of Demeter)
  (Law of Demeter,LoD),也称:最少知识原则
   一个对象应该对其它对象有最少的了解。即:一个类应该对自己需要耦合或调用的类知道得最少。你的内部是复核复杂都与我没有关系,那是你的事情,我就知道你提供这么多public方法,我就调用这么多,其它的我一概不关心。
   含义:
   (1)只和朋友交流:Only talk to your immedata friends
        每个对象都必然会与其它对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型哟哟很多,例如,组合、聚合、依赖等。出现在成员变量、方法的输入输出参数中的类成为朋友类,而出现在方法体内部的类不属于朋友类。
        方法是类的一个行为,类竟然不知道自己的行为与其它类产生依赖关系,这是不允许的,严重违反迪米特法则。不要出现GetA().GetB().GetC().GetD()之类的情况。一个方法中尽量不引入非朋友对象(JDK API提供的类除外)。
(2)朋友之间也是有距离的
       一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此,为了保持朋友间的距离,在设计时需要反复衡量:是否还可以再减少public方法和属性,是否可以修改为private、package-private、protected等访问权限,是否可以加上final关键字等。
(3)是自己的就是自己的
     如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,就放置在本类中。
(4)谨慎使用Serialiable
    
核心观念:类间解耦,弱耦合,只有若耦合了以后,类的复用率才可以提高。其要求的结果就会产生大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来难度。在采用迪米特发展时,既要让结构清晰,又要做到高内聚低耦合。不遵循时不对的,严格遵循就是过犹不及。

6、开闭原则(Open Closed Principle)

Software entities like classes,modules and functions should be open for extension but closed for modifications.
含义:一个软件应该用过扩展来实现变化,而不是修改已有代码来实现变化。

***开闭原则是一个非常虚的原则,前面5个原则是对开闭原则的具体解释,但是开闭原则并不局限于这么多,它“虚”得没有边界,就像“好好学习,天天向上”的口号一样,告诉我们要好好学习,但学什么,怎么学并没有告诉我们,需要去体会和掌握,开闭原则也是一个口号。*************

一个项目的基本路径应该是这样的:项目开发、重构、测试、投产、运维,其中的重构可以对原有得到设计和代码进行修改,运维尽量减少对原有代码的修改,保持历史代码的纯洁性,提高系统的稳定性。

-》开闭原则可以通过以下几个方面来理解其重要性:
(1)开闭原则对测试的影响
(2)开闭原则可以提高复用性
     **********如何提高代码复用率呢?缩小逻辑粒度,知道一个逻辑不可再拆分位置。*************************
(3)开闭原则可以提高可维护性
     ****维护人员最乐意做的事情就是扩展一个类,而不是修改一个类******************
(4)面向对象开发的要求

如何应用开闭原则:
(1)抽象约束
    ---通过接口和抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法
    ---参数类型、引用对象尽量使用接口或抽象类,而不是实现类
    ---抽象层尽量保持稳定,一旦确定即不允许修改
(2)元数据(metadata)控制模块行为
     元数据定义:用来描述环境和数据的数据,通俗地讲就是配置参数,参数可以从文件中获得,也可以从数据库中获得。例如,login方法中提供了这样的逻辑:先检查IP是否是在允许访问的列表中,然后再决定是否需要到数据库中验证密码,该行为就是一个典型的元数据控制模块访问的例子,其中达到极致的就是控制反转,使用最多的就是spring容器。
(3)制定项目章程
每个项目成员都必须遵守的统一规定。
(4)封装变化
   ---将相同的变化封装到一个接口或抽象类中
   ---将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。封装变化,也就是受保护的变化,找出预计有变化或不稳定的点,我们为这些变化点创建稳定的接口,准确地讲是封装可能发生的变化,一旦预测到或“第六感”发觉有变化,就可以进行封装。23个设计模式就是从各个不同的角度对变化进行封装的,我们会在各个模式中逐步讲解。


















     

猜你喜欢

转载自blog.csdn.net/jiuzaizuotian2014/article/details/79411677