设计模式原则

我们在开发的过程中,一般来说要求尽量做到代码的复用性和可维护性。代码的复用可以提高开发的效率和质量,避免重复造轮子,节约开发成本。如果代码的复用性做得好,系统的可维护性就会大大增强。在面向对象的开发中,我们进行代码设计时需要遵循设计模式的基本原则。遵循这些设计原则可以有效的提高系统的复用性、可扩展性和可维护性。

常用的设计原则包括:

单一职责原则:类的职责要单一,不能将太多的职责放在一个类中。

开闭原则:对扩展开放,对修改关闭。

里氏代换原则:父类出现的地方,子类一定也可以出现。

依赖倒置原则:要针对抽象编程,而非针对具体编程。即:依赖抽象,不依赖具体。

合成/聚合复用原则:系统中应该尽量使用组合和聚合关联关系,尽量少使用甚至不适用继承关系。

迪米特法则:一个软件实体对其它类的引用越少越好,或者说如果两个类彼此之间不直接通信,那么这两个类之间就不应当发生直接相互作用,而是通过引入一个第三者发生间接交互。

 

下面逐一讲解:

1. 单一职责原则(Single Responsibility Priciple, SRP)

定义:

一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。(Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.),即有且仅有一个原因使类变更。

简而言之:就是类的职责要单一,使类高内聚,低耦合

原则分析:
1)一个类(或者大到模块,小到方法)承担的职责越多,它被复用的可能性越小,而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。 
2)类的职责主要包括两个方面:数据职责和行为职责,数据职责通过其属性来体现,而行为职责通过其方法来体现。
3)单一职责原则是实现高内聚、低耦合的指导方针,在很多代码重构手法中都能找到它的存在,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。 
 
优劣分析:
1、可以降低类的复杂性,使类的职责清晰明确。比如数据职责和行为职责清晰明确。
2、提高类的可读性和维护性。
3、变更引起的风险减低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的类有影响,对其他接口无影响,这对系统的扩展性、维护性都有非常大的帮助。
 
2. 开闭原则(Open-ClosedPrinciple, OCP)
定义:
一个软件实体(如类、模块和函数)应该对扩展开放,对修改关闭。意思是说在一个系统或者模块中,对于扩展是开放的,对于修改是关闭的。一个好的系统是在不修改源代码的情况下,可以扩展你的功能。而实现开闭原则的关键就是抽象化.
 
原则分析:

1)当软件实体因需求要变化时, 尽量通过扩展已有软件实体,可以提供新的行为,以满足对软件的新的需求,而不是修改已有的代码,使变化中的软件有一定的适应性和灵活性 。已有软件模块,特别是最重要的抽象层模块不能再修改,这使变化中的软件系统有一定的稳定性和延续性。

2)实现开闭原则的关键就是抽象化在"开-闭"原则中,不允许修改的是抽象的类或者接口,允许扩展的是具体的实现类,抽象类和接口在"开-闭"原则中扮演着极其重要的角色,即要预知可能变化的需求,又预见所有可能已知的扩展,所以在这里"抽象化"是关键!

3)可变性的封闭原则:找到系统的可变因素,将它封装起来。 这是对"开-闭"原则最好的实现。 不要把你的可变因素放在多个类中,或者散落在程序的各个角落.。你应该将可变的因素封套起来并且切忌不要把所用的可变因素封套在一起。

 

3. 里氏代换原则(Liskov Substitution Principle, LSP)

定义:

如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有变化,那么类型S是类型T的子类型。

以上的定义太过严格且难于理解,我们换一种说法:所有引用基类(父类)的地方必须能透明地使用其子类的对象。即子类能够必须能够替换基类能够从出现的地方。子类也能在基类的基础上新增行为。

简而言之:任何基类出现的地方,子类也可以出现。

原则分析:

1)讲的是基类和子类的关系,只有这种关系存在时,里氏代换原则才存在。正方形是长方形是理解里氏代换原则的经典例子。
2)里氏代换原则可以通俗表述为:在软件中如果能够使用基类对象,那么一定能够使用其子类对象。把基类都替换成它的子类,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类(因为子类可以在基类的基础上扩展新的行为)。
3)里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
 
优劣分析:
在面向对象的语言中,继承是必不可少的、非常优秀的语言机制,它有如下优点:
1)代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
2)提高代码的重用性;
3)子类可以形似父类,但又异于父类,“龙生龙,凤生凤,老鼠生来会打洞”是说子拥有父的“种”,“世界上没有两片完全相同的叶子”是指明子与父的不同;
4)提高代码的可扩展性,实现父类的方法就可以“为所欲为”了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;
5)提高产品或项目的开放性。
继承的缺点如下:
1)继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;
2)降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;
3)增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大片的代码需要重构。
 
4. 依赖倒置原则(Dependence Inversion Principle, DIP)
定义:

高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:

1)抽象不应当依赖于细节;细节应当依赖于抽象;

2)要针对接口编程,不针对实现编程。

原则分析:

1)如果说开闭原则是面向对象设计的目标,依赖倒转原则是到达面向设计"开闭"原则的手段。如果要达到最好的"开闭"原则,就要尽量的遵守依赖倒转原则。 可以说依赖倒转原则是对"抽象化"的最好规范!

2)依赖倒转原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。 

3)类之间的耦合:零耦合关系,具体耦合关系,抽象耦合关系。依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。
 
依赖关系是一种使用关系,特性事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。例如,A类的变化引起了B类的变化,则B类依赖于A类。大多数情况下,依赖关系体现在某个类的方法使用另一个类的对象作为参数。
 
优劣分析:
采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。
 
5.合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)
定义:
又称合成复用原则(Composite ReusePrinciple或CRP),尽量使用对象组合,而不是继承来达到复用的目的。
就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。简而言之,要尽量使用合成/聚合,尽量不要使用继承。

原则分析:

1)在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。
继承复用:
  实现简单,易于扩展。破坏系统的封装性;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用
组合/聚合复用:
  耦合度相对较低,选择性地调用成员对象的操作;可以在运行时动态进行。(“黑箱”复用
2)组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承。
在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。
3)此原则和里氏代换原则氏相辅相成的,两者都是具体实现"开-闭"原则的规范。违反这一原则,就无法实现"开-闭"原则。
 
下面介绍合成和聚合的概念:
合成(组合):表示一个整体与部分的关系,指一个依托整体而存在的关系(整体与部分不可以分开,整体与部分具有统一的生命周期)。一旦整体对象不存在,部分对象也将不存在,部分对象与整体对象之间具有同生共死的关系。例如人与人的头部和四肢。
聚合:聚合是比合成关系的一种更强的依赖关系,也表示整体与部分的关系(整体与部分可以分开)。
 
6. 迪米特法则(Law Of Demeter, LoD)
定义:
又名最少知道原则(Least Knowledge Principle或简写为LKP)。

几种形式定义:

(1) 不要和“陌生人”说话。英文定义为:Don't talk to strangers.

(2) 只与你的直接朋友通信。英文定义为:Talk only to your immediatefriends.

(3) 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

简单地说,也就是,一个对象应当对其它对象有尽可能少的了解。一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的public方法,我就调用这么多,其他的一概不关心。

 

 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2016-09-06 22:00  GloryLee  阅读(299)  评论(0编辑  收藏  举报