page contents

面向对象设计基本原则

引言   在软件开发过程中,随着系统复杂度的增加,业务的变更,程序员面临着耦合性,内聚性,可维护性,可扩展性,可重用性的挑战。而良好的设计模式就可以解决此类问题。设计模式是包含...

640?wx_fmt=jpeg


引言

  在软件开发过程中,随着系统复杂度的增加,业务的变更,程序员面临着耦合性,内聚性,可维护性,可扩展性,可重用性的挑战。而良好的设计模式就可以解决此类问题。设计模式是包含了面向对象的精髓,熟练掌握面向对象设计/分析是掌握设计模式的基础。可以帮助我们考虑代码结构,使代码易于维护、易于扩展、易于阅读。

  设计模式本身也是有自己需要遵守的原则,也是各种设计模式的基础。那么设计模式常用的七大原则是什么?


设计模式有常用的七大原则

 

一、单一职责

    一个类,只做一件事并且做好

 特点:

  • 一个类只负责一件事,可以降低类的复杂性。

  • 提高类的可读性,可维护性就提高了。

  • 降低变更引起可能出现的风险,如果接口的单一职责做得好,一个接口修改只对应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。

    例如我们 web开发中常用的 MVC 开发模式就是单一职责原则的体现。

  • Model 层提供数据。

  • View 层专注于前端视图的展示。

  • Controller层专注业务逻辑。


二、接口分离原则

     为各个类建立他们所需要的专门的接口。把一个接口切分为多个接口,把一个大的职责切分为小职责以及这些职责之间的协作交互。比如一个客户端不应该依赖它不需要的接口,即一个类对另外一个类的依赖应该建立在最小接口上。

    1).接口要尽量小。

    这是接口隔离原则的核心定义,不出现臃肿的接口(Fat Interface),但是“小”是有限度的,首先就是不能违反单一职责原则。

 根据接口隔离原则拆分接口时,首先必须满足单一职责原则。

    2).  接口要高内聚。

    高内聚就是要提高接口、类、模块的处理能力,减少对外的交互。具体到接口隔离原则就是,要求在接口中尽量少公布public方法,接口是对外的承诺,承诺地越少对系统开发越有利,变更的风险也就越少,同时也有利于降低成本。

    3).  定制服务。

 定制服务就是单独为一个个体提供优良的服务。

    4). 接口设计是有限度的。

    接口的设计粒度越小,系统越灵活,这是不争的事实。但是,灵活的同时也带来了结构的复杂化,开发难度增加,可维护性降低,这不是一个项目或产品所期望看到的,所以接口设计一定要注意适度,这个度只能根据经验和常识判断,没有一个固化或可测量的标准。

 单一职责和接口分离原则的共同点:

  • 都是提高类的内聚性,降低耦合性。

单一职责和接口分离原则的不同点:


    三、开放封闭原则

    一个类应该是对扩展是开放的,对类的内部修改是封闭的,不应该通过修改来增加类的功能,而是通过扩展来增加功能。

    比如一个类中有两个功能,现在要增加第三个功能,那么这个新增加的功能不能对另外两个功能有影响。

    开放封闭原则是面向对象中最重要的原则,相当于一个总纲领。


    四、依赖倒置原则

    这条原则说明两个基本的要点:

  • 高阶的模块不应该依赖低阶的模块,它们都应该依赖于抽象

  • 抽象不应该依赖于实现,实现应该依赖于抽象

    一个类不应该强依赖另外一个类,每个类对于另外一个类都是可以替换的。高层模块不需要依赖底层的模块。

     比如A类和B类,A类需要B类的相关功能,A类不应该在其内部中直接调用B类,而是应该使用依赖注入的方式通过注入的方式,来将B类的对象注入到A类中,这样B类对于A类来说是可以替换的。高层模块也不应该依赖低层模块,二者都应该依赖其抽象,都是面向接口编程,而不是面向具体实现。接口一般都是稳定的。使用接口或者抽象类可以更好的制定一些规范和契约,而不去涉及具体的操作,具体的实现交给具体的类来完成。

640?wx_fmt=png

五、里氏替换原则

    继承必须确保父类中所拥有的所有性质在子类中依然成立。就是子类可以扩展父类的功能,子类尽量添加新的方法不要改变父类原有的功能,程序中对于实例化对象的子类型,不需要修改代码,可以直接进行替换。

 有个著名的说法:正方形不是长方形。

    Liskov于1987年提出了一个关于继承的原则 “Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”(继承必须确保超类所拥有的性质在子类中仍然成立)。

 就是说,当一个子类的实例应该能够替换任何其父类的实例时,它们之间才具有is-A关系。


六、合成复用原则

    尽量使用组合或者聚合等关联关系来实现,其次在考虑使用继承关系来实现。

    组合是Has-A的关系,继承是Is-A关系。“Is-A”是严格的分类学意义上的定义,意思是一个类是另以个类的“一种”。而“Has-A”表示某一个角色具有某一项责任。比如狗和动物是继承关系,狗不能和动物用组合,因为狗不是动物组成的。

   合成复用原则和里氏替换原则是相辅相成的,如果要使用继承关系,则必须严格遵循里氏替换原则。

    通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点:

  • 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。

  • 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

  • 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

    采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:

  • 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。

  • 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。

  • 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。


    七、迪米特法则,又称之为最少知识原则

    定义:只与你的直接朋友交谈,不跟“陌生人”说话。

    其含义是:如果两个类之间无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,尽量减少对其他类的依赖,提高模块的相对独立性。

  通俗的来讲,就是描述一个类对自己依赖的类知道的越少越好,无需关心是怎么实现的,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。

迪米特法则还有一个更简单的定义:只与直接的朋友通信。

     首先来解释一下什么是直接的朋友:迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

    只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。 

    所以一般应用迪米特法则设计的类都有一些特点,减少暴露类成员,类本身不能被改变,降低对类的访问权限等。

  设计模式的门面模式(Facade)和中介者模式(Mediator),都是迪米特法则应用的例子。

  迪米特法则优点:

  • 降低了类之间的耦合度,提高了模块的相对独立性。

  • 由于亲合度降低,从而提高了类的可复用性和系统的扩展性。

     迪米特法则缺点:

  • 过分的使用迪米特原则,会产生大量这样的中间类和传递类,导致系统复杂度变大。

  使用原则:

  • 使用迪米特原则时候要权衡好,保证系统架构的清晰。


    八、其他的设计原则

  • 配置化:尽可能的使用配置,而不要使用硬编码的方式。比如像一些数据库参数及常量,应该放到配置文件中。

  • 面向接口编程:只需要关心接口,不需要关心具体的实现。

  • DRY(Don't Repeat Yourself )避免重复代码:抽取重复的代码封装成一个函数或者类方法,保持里面的逻辑一致,可以被其他使用者调用。

  • 发表于 2020-01-04 15:43
  • 阅读 ( 572 )
  • 分类:PHP开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1312 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章