第2章 设计原则

2022-06-01  本文已影响0人  一介书生独醉江湖
设计原则主要有6个:
■ 单一职责原则
■ 里氏替换原则
■ 依赖倒置原则
■ 接口隔离原则
■ 迪米特法则
■ 开闭原则
■ 单一职责原则
一个类,应当只有一个引起它变化的原因;即一个类应该只有一个职责,只专注于做一件事;
单一职责原则的优点:
■ 降低类的复杂性;
■ 提高类的可读性;
■ 提高代码的可维护性和复用性;
■ 降低因变更引起的风险;
单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计是否优良,但“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异;
■ 里氏替换原则
在面向对象的语言中,继承是必不可少的、优秀的语言机制;
里氏替换有两种定义:
■ 一、如果对一个类型为S的对象o1,都有类型为T的对象o2,使得以S定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T是类型S的子类型;
■ 二、所有引用基类的地方必须能透明地使用其子类对象。清晰明确地说明只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道父类还是子类;但是反过来则不可以,有子类的地方,父类未必就能适应;
继承的优点:
■ 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
■ 提高代码的可重用性;
■ 提高代码的可扩展性;
■ 提高产品或项目的开放性;
继承的缺点:
■ 继承是入侵式的。只要继承,就必须拥有父类的所有属性和方法;
■ 降低代码的灵活性。子类必须拥有父类的属性和方法,使子类受到限制;
■ 增强了耦合性。当父类的常量、变量和方法修改时,必须考虑子类的修改,这种修改可能造成大片的代码需要重构。
从整体上看,继承的“利”大于“弊”,然而如何让继承中“利”的因素发挥最大作用,同时减少“弊”所带来的麻烦,这就需要引入“里氏替换原则”
在编译期,Java语言编译器会检查一个程序是否符合里氏替换原则,这是一个无关实现的、纯语法意义上的检查;
如果违反了里氏替换原则,Java编译器根本不会让这样的程序编译通过;
里氏替换原则为良好的继承定义了一个规范:
■ 子类必须完全实现父类的方法;
■ 子类可以有自己的个性;
■ 覆盖或实现父类的方法时输入参数可以被放大;
■ 覆盖或实现父类的方法时输出结果可以被缩小;
设计模式中体现里氏替换原则的有:
■ 策略模式
■ 组合模式
■ 代理模式
■ 依赖倒置原则
原始定义的三层含义:
■ 高层模块不应该依赖低层模块,两者都依赖其抽象;
■ 抽象不依赖细节;
■ 细节应该依赖于抽象;

■ 依赖倒置原则更加精确的定义就是“面向接口编程”——OOD(Object-OrientedDesign)的精髓之一,是JavaBean、EJB和COM等组件设计模型背后的基本原则;
依赖倒置原则在Java语言中的表现:
■ 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生;
■ 接口或抽象类不依赖于实现类;
■ 实现类依赖于接口或抽象类;
依赖倒置原则的本质:
就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,互不影响,实现模块间的松耦合;
在项目中使用这个原则只要遵循以下几个规则:
■ 每个类尽量都具有接口或抽象类,或者抽象类和接口两者都具备。这是依赖倒置的基本要求,接口和抽象类都是抽象的,有了抽象才可能有依赖倒置;
■ 变量的表面类型尽量是接口或者是抽象类;
■ 任何类都不应该从具体类派生;
■ 尽量不要重写基类的方法。如果基类是一个抽象类,而且这个方法已经实现了,子类尽量不要重写。类之间依赖的是抽象,重写了非抽象方法,对依赖的稳定性会产生一定的影响;
■ 结合里氏替换原则使用。里氏替换原则指出父类出现的地方子类就可以出现,结合依赖倒置原则可以得出一个通俗的规则:接口负责定义抽象方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确地实现业务逻辑,同时在适当的时候对父类进行细化;
依赖倒置原则是6个设计原则中最难以实现的原则,它是实现开闭原则的重要途径,依赖倒置原则没有实现,就不能实现对扩展的开放,对修改关闭。在项目中,只要抓住“面向接口编程”就基本抓住了依赖倒置的原则;
■ 接口隔离原则
第一种定义:
■ 客户端不应该依赖它不需要的接口;
第二种定义:
■ 类间的依赖关系应该建立在最小的接口上;
■ 实例接口(Object Interface),在Java中声明一个类,然后用new关键字产生一个实例,它是对一个类型的事物所具有的方法特征的描述,也称做一个“接口”,这仅是一种逻辑上的抽象。例如,定义一个Person类,使用Personzhangsan = new Person()产生一个实例,该实例遵从的标准是Person这个类,Person就是zhangsan的接口;
■ 类接口(Class Interface),是指在 Java 中使用 interface 严格定义的接口,例如, java.lang.Runnable就是一个Java线程接口;
接口隔离原则的具体含义:
■ 一个类对另外一个类的依赖性应当是建立在最小的接口上的;
■ 一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。因此使用多个专门的接口比使用单一的总接口要好。
■ 不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构,即不要强迫客户使用它们不用的方法,否则这些客户就会面临由于这些不使用的方法的改变所带来的改变;
接口隔离原则和其他的设计原则一样,都是需要花费时间和精力来进行设计和筹划的,但是它带来了设计的灵活性,并降低整个项目的风险,当业务变化时能够快速应付。在设计接口时应根据经验和常识决定接口的粒度大小,如果接口粒度太小,导致接口数量剧增,给开发带来难度;如果接口粒度太大,灵活性降低,无法提供定制服务,给项目带来无法预计的风险;
■ 迪米特法则
迪米特法则又叫最少知识原则(Least Knowledge Principle,LKP),意思是一个对象应当对其他对象尽可能少的了解;
具有代表性的是以下几种表述:
■ 只与你直接的朋友们通信(Only talk to your immediate friends);
■ 不要跟“陌生人”说话(Don't talk to strangers);
■ 每一个软件单位对其他的单位都只有最少的了解,这些了解仅局限于那些与本单位密切相关的软件单位;
迪米特法则的设计模式有:
■ 外观模式
■ 中介者模式
■ 开闭原则
一个软件实体应当对扩展开放,对修改关闭;
开闭原则的重要性:
■ 开闭原则提高复用性;
  在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑,代码粒度越小,被复用的可能性就越大,避免相同的逻辑重复增加。开闭原则的设计保证系统是一个在高层次上实现了复用的系统。
■ 开闭原则提高可维护性;
  一个软件投产后,维护人员的工作不仅仅是对数据进行维护,还可能对程序进行扩展,就是扩展一个类,而不是修改一个类。开闭原则对已有软件模块,特别是最重要的抽象层模块要求不能再修改,这就使变化中的软件系统有一定的稳定性和延续性,便于系统的维护。
■ 开闭原则提高灵活性;
  所有的软件系统都有一个共同的性质,即对系统的需求都会随时间的推移而发生变化。在软件系统面临新的需求时,系统的设计必须是稳定的。开闭原则可以通过扩展已有的软件系统,提供新的行为,能快速应对变化,以满足对软件新的需求,使变化中的软件系统有一定的适应性和灵活性。
■ 开闭原则易于测试;
  测试是软件开发过程中必不可少的一个环节。测试代码不仅要保证逻辑的正确性,还要保证苛刻条件(高压力、异常、错误)下不产生“有毒代码”(Poisonous Code),因此当有变化提出时,原有健壮的代码要尽量不修改,而是通过扩展来实现。否则,就需要把原有的测试过程回笼一遍,需要进行单元测试、功能测试、集成测试,甚至是验收测试。开闭原则的使用,保证软件是通过扩展来实现业务逻辑的变化,而不是修改。因此,对于新增加的类,只需新增相应的测试类,编写对应的测试方法,只要保证新增的类是正确的就可以了。
虽然我们不可能做到百分之百的封闭,但是在系统设计的时候,我们还是要尽量做到这一点。开闭原则是面向对象设计的终极目标,其他设计原则都可以看做是开闭原则的实现方法;
■ 小结
■ 单一职责原则SRP(Single Responsibility Principle):一个类,只有一个引起它变化的原因,应该只有一个职责;
■ 单一职责原则提出一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计是否优良,但“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异;
■ 里氏替换原则LSP(Liskov Substitution Principle):所有引用基类的地方必须能透明地使用其子类对象,反之则不行;
■ 在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则;
■ 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系替代继承;
■ 依赖倒置原则DIP(Dependence Inversion Principle):高层模块不应该依赖低层模块,两者都应依赖其抽象,抽象不依赖细节,而细节依赖抽象;
■ 依赖倒置原则在Java中的表现是:模块间的依赖通过抽象产生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生,接口或抽象类不依赖于实现类,实现类依赖接口或抽象类;
■ 接口隔离原则ISP(Interface Segregation Principle):一个类对另外一个类的依赖性应当是建立在最小的接口上,使用多个专门的接口比使用单一的总接口要好;
■ 迪米特法则LoD(Law of Demeter):一个对象应该对其他对象有最少的了解,即一个类应该对自己需要耦合或者调用的类知道得最少;
■ 开闭原则OCP(Open-Close Principle):一个软件实体,如类、模块和函数应该对外扩展开放,对修改关闭。
参考:
摘录 《设计模式(Java版)》韩敬海主编;(微信读书APP中有资源,可以直接阅读)
上一篇 下一篇

猜你喜欢

热点阅读