程序设计:设计模式:六大原则

总结

设计模式

  • 开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭;
    • 里氏替换解决重写父类造成的可复用性变差
    • 合成复用解决继承中封装和耦合性问题
  • 里氏替换原则告诉我们不要破坏继承体系;
  • 依赖倒置原则告诉我们要面向接口编程;
  • 单一职责原则告诉我们实现类要职责单一;
  • 接口隔离原则告诉我们在设计接口的时候要精简单一;
  • 迪米特法则告诉我们要降低耦合度;
  • 合成复用原则告诉我们要优先使用组合或者聚合关系复用,少用继承关系复用。

定义:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展


为什么使用开闭原则:

  • 开闭原则是基础原则,其他原则是对开闭原则的具体实现
  • 减少添加代码对测试的影响
  • 开闭原则可以提高复用性
    • 小粒度被复用性高
  • 开闭原则可以提高维护性
  • 面向对象开发的要求

如何使用开闭原则

  • 抽象约束
    • 不允许出现在接口或抽象类中不存在的public方法
    • 参数类型,引用对象尽量使用接口或抽象类,而不是实现类(里氏替换原则)
    • 抽象层尽量保持稳定,一旦确定就不要修改
  • 元数据(metadata)控件模块行为
    • 尽量使用元数据来控制程序的行为(配置参数,参数可以从文件中获得)
  • 制定项目章程
  • 封装变化
    • 将相同的变化封装到一个接口或抽象类中
    • 将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中

定义:里氏代换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。


为什么使用:

  • 克服继承的缺点
    • 优点:继承实现了代码共享,子类拥有父类方法属性,也可以改写,提高扩展性和开放性
    • 缺点:继承增强耦合,父类的常量方法属性被修改时子类被动被修改

如何使用:

  • 在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象
  • 子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法
  • 尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法

实现:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 子类中可以增加自己特有的方法
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。


为何使用:

  • 降低类间的耦合性。
  • 提高系统的稳定性。
  • 减少并行开发引起的风险。
  • 提高代码的可读性和可维护性。

如何使用:

  • 每个类尽量提供接口或抽象类,或者两者都具备
  • 变量的声明类型尽量是接口或者是抽象类
  • 任何类都不应该从具体类派生
  • 使用继承时尽量遵循里氏替换原则

客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上


为何使用:

  • 提高类的内聚性、降低它们之间的耦合性
  • 约束接口、降低类对接口的依赖性

优点:

  • 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  • 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  • 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  • 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  • 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

实现方式:

  • 接口尽量小,但是有限度,一个接口只服务一个子模块或者业务逻辑
  • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法
  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情

定义:迪米特法则(Law of Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用。


为何使用:

  • 降低类之间的耦合度,提高模块的相对独立性。
  • 尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。

注意:

  • 尽量创建松耦合的类
  • 一个类都应当尽量降低其成员变量和成员函数的访问权限
  • 只要有可能,一个类型应当设计成不变类
  • 在对其他类的引用上,一个对象对其他对象的引用应当降到最低

优点:

  • 降低了类之间的耦合度,提高了模块的相对独立性
  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性

缺点:

  • 过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低

实现:

  • 只依赖应该依赖的对象
  • 只暴露应该暴露的方法
  • 具体实现:
    • 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标
    • 在类的结构设计上,尽量降低类成员的访问权限
    • 在类的设计上,优先考虑将一个类设置成不变类
    • 在对其他类的引用上,将引用其他对象的次数降到最低
    • 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)
    • 谨慎使用序列化(Serializable)功能

定义:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。


为何使用:

  • 一个类承担的职责越多,它被复用的可能性就越小
  • 职责变化时,可能会影响其他职责的运作
  • 将不同的变化原因封装在不同的类中
  • 多个职责总是同时发生改变则可将它们封装在同一类
  • 是实现高内聚、低耦合的指导方针

优点

  • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
  • 提高类的可读性,提高系统的可维护性;
  • 变更引起的风险降低

实现:

  • 当变化发生,只影响其中一个职责,那就需要拆分
  • 如果变化都影响到这两个职责,那就不需要拆分。

合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现

为何使用:

  • 继承复用简单和易实现,但是有缺点:
    • 破坏了类的封装性,将父类的实现细节暴露给子类,父类对子类是透明的
    • 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化
    • 限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

如何使用:

  • 将已有的对象纳入新对象中,作为新对象的成员对象来实现
  • 新对象可以调用已有对象的功能,从而达到复用
  • 程序设计/设计模式/六大原则.txt
  • 最后更改: 2022/09/28 23:25
  • 39.173.107.126