java与模式笔记(3.2)——原则篇之变化中的不变,开闭原则下的5大原则

来源:互联网 发布:拍立得什么牌子好 知乎 编辑:程序博客网 时间:2024/05/20 05:10

里氏代换原则
  严格表达是:如果对于一个类型T1的一个对象o1,都有类型T2的一个对象o2,使得以T1定义的所有程序P在所有对象o1都替换成o2时,P都没有变化,那么类型T2是类型T1的子类。换言之,如果一个程序使用的是是一个基类的话,那么他一定适用于其子类,而且根本不会察觉到其子类。

  应用里氏代换原则来论证正方形不是长方形的子类的一个论断:如果正方形继承了长方形,那么一个函数类似当长小于宽的时候不断加一直到相等为止的函数就无法适从长方形一直沿用到正方形,于是,违反里氏代换原则。也就是说,根据里氏代换原则,通常的数学法则跟生活法则有着不可混淆的差别。

依赖倒转原则
  我们先看看为什么要到转。首先一个抽象类的设计是系统的宏观的统筹规划,而具体的实现诸如具体的类使得很多的算法和执行不同于其他,显然的,具体的事现是战术性的,是可变的,也是非常容易出错的,或者说是经常需要我们修改的。然而想象我们的战术发生变化之后,竟然会印象到宏观的统筹规划。这就使得我们不得不把从抽象到具体的依赖倒转过来。然而,从过程化的思维来说,我们复用的重点是低层次模块的复用,比如算法,数据结构,函数库,用相同的函数和算法来实现相同的功能,也就是说的具体实现。使得较高层次的结构依赖较低模块的结构,如此反复,一直依赖到每一行代码。致使低层次的修改直接影响高层记得逻辑。

  抽象不应当依赖于细节,细节应当依赖于抽象。
  GOF:要针对接口编程,不要针对实现编程。
  针对接口编程就是说,应当用java的接口和抽象java类,进行变量类型的声明,参数类型的声明,方法返回类型的声明,以及类型的转换。
  不要针对实现编程就是说,不应用具体的java类,进行变量类型的声明,参数类型的声明,方法返回类型的声明,以及类型的转换。
  要做到这点,一个java的类,应该只实现接口和抽象类声明的方法,而不应该给出多余的方法,引用对象的抽象类型。具体的做法就是要声明的时候假设蛋是鸡的父类,如果要实例化一个鸡,应该:蛋 x = new 鸡();而不是 鸡 x = new 鸡();

  怎样做到依赖倒转原则?以抽象的方式耦合是依赖倒转原则的关键。
  依赖倒转原则是OO设计的核心原则,所有模式的研究和应用都是以依赖倒转原则为指导原则。

  谈到这里不得不说到接口,熟知的接口和抽象类各有优势和缺点:
  1)抽象类可以实现具体的方法,而借口不行,这样就使得抽象类添加一个实现的方法更为容易,恐怕这是抽象类的唯一的优点了。
  2)接口,由于一个具体的类只能拥有一个超类,所以,接口就在这里弥补了一个类的定义时候要匹配多类型的缺陷了。
一般来说,如果需要一个类来适应不同的类型,那么它会继承一个超类然后继承需要的所有接口。

  同样,依赖倒转原则也有自己的缺点,就是他认为所有的具体类都是可变的,这也是不正确的,有些时候客户完全可以信任某些不会变化的具体的类。

接口隔离原则
  使用多个专门的接口,比使用单一的总的接口好。
  从一个客户类的角度来讲,一个类对另外一个类的依赖性应该是建立在最小的接口上。
  将接口理解为一个类提供的所拥有方法的特征的集合,也就是一种逻辑上才有的概念。这样接口的划分就直接带来类型的划分。一个接口相当于舞台上一个角色,一个角色应该由一个具体的实例人来扮演。于是,一个接口应该只代表一个角色,而不是多个角色。将这种角色隔离的原则叫角色隔离原则。

  这里,作者认为,一个方法的集合是接口,也就是角色。
  要根据客户是谁,使用不同的对外方法组,也就是接口。
  任何一个软件实体,除非绝对需要,否则不要与外界联系。
  Public就是一个承诺,不要向客户提出不必要的承诺。软件实体的接口也是一样。
  定制服务是这里的主要的设计原则。如果客户软件仅仅需要一些方法的话,那么就不要提供这些方法以外的方法,那是滥用。

合成/聚合复用原则
  在复用的时候,要分清is a and has a的区别,知道了这个就知道何时使用合成聚合什么时候使用继承了。
  合成和聚合的分别:
  术语上讲是:在一个合成的关系里,整体与部分的生命周期是一样的。一个合成的关系中的成分对象是不能与另外一个合成关系共享的。
  同属的说法是,合成的关系是孙悟空和他的手脚的关系,聚合是孙悟空和金箍棒的关系。


迪米特法则
  最少知识原则,一个对象应当对其他对象有尽可能少的了解。只与你直接的朋友说话。不要跟陌生人说话。每一个软件单位都对其它软件单位只有最少的知识,而且局限于那些只与本单位软件密切相关的软件单位上。

狭义的迪米特法则:
  一个人和朋友组成了圈子,遇到一个陌生人,需要联系,但是它不应该跟陌生人联系,只跟朋友联系,要通过朋友根陌生人联系,从而显得他会认为这个联系其实只是她跟朋友之间的联系,并不是根陌生人的联系。
  朋友圈子的限定:
     当前对象的本身;
     以参数形式传递到当前对象方法中的对象;
     当前对象实例直接引用的对象;
     当前对象如果是一个聚集,那么它成员都是他的朋友;
     当前对象所创建的对象。

  狭义迪米特法则有个非常大的缺点,就是使得在系统中产生大量的小方法,而且还与业务逻辑无关。解决这个缺陷我们一般使用的重构方法是给自己加一个虚拟的陌生人接口使得自己与陌生人之间有个联系的渠道,从而避开朋友。

  迪米特法则主要是控制信息的过载。要注意:
     在类的划分上要创建弱耦合的类;
     在类的结构设计上,一个类要尽可能降低自己成员的访问的权限,也就是保护好自己的private成员。
     在类的设计上,如果可能,要尽可能设计成不变类。
     对其他类的饮用上,一个对象要对其他类的引用降低到最低。

  尽量将一个类的方法设计成private的,除非包里有需要调用这个方法的类,才设计成package-private的,当发现一个类有很多package-private的函数的时候,就要考虑这个类是否划分的不合理。