面向对象设计原则

2023-05-16

面向对象设计原则是设计模式的基础,每一个设计模式都符合某一个或者多个面向对象设计原则,面向对象设计原则是用于评价一个设计模式的使用效果的重要指标之一。

最常见的 7 大设计原则

单一职责原则

一个对象应该只包含单一的职责,并且该职责被完整的封装在一个类中
在软件系统中,一个类承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责越多,相当于将这些职责耦合在一起,当其中一个职责发生变化时可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中。下面通过一个简单的实例进一步分析单一职责原则:

某 CRM 系统中客户信息图形统计模块提出了如下的初始设计方案

在 CustomerDataChart 类的方法中,getConnection()方法用于连接数据库,findCustomers()用于查询所有的客户信息,createChart()方法用于创建图表,displayChart()用于显示图表。很明显 CustomerDataChart 类承担了太多的职责,现使用单一职责原则对其进行重构。

通过对当前类中的各种职责进行分析可知,主要有三种:获取数据库连接、从数据库获取数据、创建和展示图表。因此可拆分为 3 个类,分别是:

  • DBUtil: 数据库连接管理类,负责连接数据
  • CustomerDAO:数据库操作类,负责从数据库获取数据
  • CustomerDataChart:图表操作类,负责创建和展示图表
    使用单一职责原则重构后的类图设计如下:

在这里插入图片描述

开闭原则

开闭原则:软件实体应当对扩展开放,对修改关闭
软件实体可以指一个软件模块、一个由多个类组成的局部结构或者一个独立的类。为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。

在 Java 等面向对象编程语言中,都提供了接口、抽象类等机制,可以通过他们定义系统的抽象层,在通过具体类来进行扩展,如果需要修改系统的行为,无须对抽象层进行改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。

里氏代换原则

里氏代换原则:所有引用基类的地方必须能透明地使用其子类对象
里氏代换原则表明,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立。

在运用里氏代换原则时,应该将父类设计为抽象类或者接口,让子类继承父类或者实现父接口,并实现父类中声明的方法,在运行时子类实例替换父类对象,可以方便地扩展系统的功能,无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。

依赖倒转原则

依赖倒转原则:高层模块不应该依赖底层模块,它们都应该依赖抽象,抽象不应该依赖于细节,细节应该依赖于抽象
简单来说,依赖倒转原则要求针对接口编程,而不是针对实现编程。

依赖倒转原则要求在程序代码中传递参数时或在关联关系中尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不用具体类来做这些事情。一个具体的类应当只实现接口或者抽象类中声明过的方法,而不要给出多余的方案,否则将无法调用到在子类中增加的新方法。

在实现依赖倒转原则时需要针对抽象层编程,而将具体类的对象通过依赖注入的方式注入到其他对象中,常用的注入方式有 3 种,分别是:构造注入、设值注入和接口注入

在这里插入图片描述

下面通过一个简单的实例来加深对开闭原则、里氏代换原则和依赖倒转原则的理解:

某 CRM 系统中经常需要将存储在 TXT 或 Excel 的文件中的客户信息转存到数据库中,因此需要进行数据格式转换,在客户数据操作类 CustomerDAO中将调用数据格式转换类的方法来实现格式转换,初始方案设计如下:

在这里插入图片描述

分析现有的设计,发现由于每次转换的数据来源不一定相同,因此需要经常更换数据转换类,因此需要修改 CustomerDAO代码,而且在引入并使用新的数据转换类时也不得不修改 CustomerDAO的代码,系统扩展性较差,违反了开闭原则,因此需要进行重构。如下图所示:

在这里插入图片描述

重构后,引入了抽象数据转换类,CustomerDAO针对抽象类DataConvertor编程,而将具体数据转换类名存储在配置文件中,符合依赖倒转原则。根据里氏代换原则,程序运行时具体数据转换类对象替换 DataConvertor类型的对象,程序不会产生任何异常。在更换具体数据转换类时无须修改源代码,只需要修改配置文件。如果需要增加新的数据转换类,只要将新增的数据转换类作为 DataConvertor的子类并修改配置文件即可,原有代码无须修改,满足开闭原则。

在重构过程中同时使用了开闭原则,里氏代换原则和依赖倒转原则,三者一般是同时出现,相辅相成。总结来说就是:开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段

接口隔离原则

接口隔离原则:客户端不应该依赖那些它不需要的接口
对于“接口”,这里有两种具体的含义:

一种是指某种编程语言中的interface 类型
另一种是指一个类型所有具有的方法特征的集合
在本文中主要指第二种,具体来说就是定义接口时要尽可能提供职责唯一的接口,而不是大而全的接口,因为实现一个接口就得实现该接口的所有方法。大而全的接口不利于系统的维护与扩展。但同时也要控制接口粒度不能太细,否则可能会导致“接口爆炸”。

合成复用原则

合成复用原则:又称组合/聚合复用原则,优先使用对象组合,而不是通过继承来达到复用的目的
合成复用原则就是在一个新的对象里通过关联关系来使用一些已有对象,使之成为新对象的一部分。简而言之就是在复用对象时多用组合或者聚合,少用继承。

继承 VS 组合/聚合

继承复用会破坏系统的封装性,因为继承会将类的实现细节暴露给子类,由于基类的某些内部细节对子类来说是可见的,因此这种复用又称为“白箱”复用,如果基类发生改变,那么子类的实现也不得不发生改变。
组合/聚合复用可以调用已有对象的功能,但是成员对象的内部实现细节对新对象是不可见的,因此这种复用又称为“黑箱”复用,对比继承复用而言,其耦合度更低,成员对象的变化对新对象的影响不大,而且合成复用可以在运行时动态进行,新对象可以动态的引用与成员对象类型相同的其他对象。
一般而言,如果两个类之间是“Has-A”的关系应使用组合/聚合,如果是“Is-A”的关系可以使用继承。

迪米特原则

迪米特原则:每个软件单位对其他单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位
迪米特原则要求一个软件实体应该尽可能少地与其他实体发生相互作用,从而降低系统的耦合度,使类与类之间保持松散的耦合关系。

迪米特原则有一种通俗的说法是“不要和陌生人说话”。意思是只与自己的朋友发生通信,对于“陌生人”不应该有直接的通信。“朋友”范围包括以下几类:

1、当前对象本身

2、以参数形式传入到当前对象方法中的对象

3、当前对象的成员对象

4、如果当前对象的成员对象是一个集合,那么集合中的元素也是朋友

5、当前对象所创建的对象

只要满足上述情况之一,就是当前对象的“朋友”,如果两个不必要的对象需要通信,则可以通过“第三方”对象来进行转发调用。因此将迪米特原则运用到系统设计中时,需要注意:

1、在类的划分上要尽量松耦合

2、每个类都应该尽可能降低其成员变量和成员函数的访问权限

总结

  • 单一职责原则要求在软件系统中一个对象只包含单一职责,并且该职责被完整地封装在一个类中
  • 开闭原则要求软件实体应该对扩展开放,对修改关闭
  • 里氏代换原则要求在软件中所有引用到基类的地方必须能透明地使用其子类的对象
  • 依赖倒转原则要求高层模块不依赖底层模块,他们都应该依赖抽象,抽象不依赖细节,细节应该依赖抽象
  • 接口隔离原则要求客户端不应该依赖那些它不需要的接口
  • 合成复用原则要求优先使用对象组合,而不是通过继承来达到复用的目的
  • 迪米特原则要求每一个软件单位对其他单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

面向对象设计原则 的相关文章

  • PyQt——QListWidget

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QListWidget 例
  • PyQt——QTabWidget

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QTabWidget 例子
  • PyQt——QStackedWidget

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QStackedWidge
  • PyQt——QDockWidget

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QDockWidget 例
  • lingo入门教程之二 --- 集合运用

    lingo中的集合用法很多 xff0c 这里主要通过几个例题来进行讲解 对于每一个问题 xff0c 都要先找到对应的目标函数 xff0c 然后对相应值进行初始化 xff0c 然后找到约束条件等进行求解 例1 xff1a SAILCO公司需要
  • PyQt——MDI(多文档界面)

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QMdiArea 例子 3
  • PyQt——QScrollBar

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QScrollBar 例子
  • PyQt——QTimer

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQT5中 QTimer例子 39 3
  • PyQt——QThread

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQT5中 QThread 例子 39
  • PyQt——事件处理

    PyQt事件处理 前言1 低级的事件处理程序2 事件处理机制1 事件机制与信号槽机制的区别2 事件处理的方法3 事件处理实例 前言 1 低级的事件处理程序 span class token comment coding utf 8 span

随机推荐

  • python——精华技巧

    1 大量的if else结构采用字典key来匹配 days span class token operator 61 span span class token string 34 Mon Tue Wed Thu Fri Sat Sun 3
  • Python使用struct处理二进制(pack和unpack用法)

    python有时需要处理二进制数据 xff0c 例如 存取文件 xff0c socket操作时 这时候 xff0c 可以使用python的struct模块来完成 可以用struct来处理c语言中的结构体 一 struct模块中最重要的三个函
  • PyQt——QGroupBox

    QGroupBox为构建分组框提供了支持 分组框通常带有一个边框和一个标题栏 xff0c 作为容器部件来使用 xff0c 在其中可以布置各种窗口部件 布局时可用作一组控件的容器 xff0c 但是需要注意的是 xff0c 内部必须使用布局控件
  • PyQt——高清屏幕自适应设置

    Qt Designer 设计界面 xff1a 在高清屏未设置AA EnableHighDpiScaling的预览界面 xff1a 布局字体控件尺寸上明显存在偏差 设置了AA EnableHighDpiScaling的预览界面 xff1a 运
  • PyQt——实现多窗口

    主窗口通过按钮显示子窗口 1 使用qtdesigner设计窗口 主窗口 xff1a main window 子窗口 xff1a Dialog 生成的是ui文件 2 为两个窗口生成py文件 3 创建两个窗口的继承类 span class to
  • 《统计学习方法》各章节代码实现与课后习题参考解答

    待完成 统计学习方法 各章节代码实现与课后习题参考解答 章节代码课后习题第1章 统计学习方法概论 LeastSquaresMethod 传送门传送门 第2章 感知机 Perceptron 传送门传送门第3章 k近邻法 KNearestNei
  • Opencv的鼠标事件

    span class token keyword import span cv2 span class token keyword import span numpy span class token keyword as span np
  • python多线程之事件触发(线程间通信)

    span class token keyword import span threading event span class token operator 61 span threading span class token punctu
  • 架构规范

    系统架构规范 目录一 架构规范二 数据库规范三 SOA规范四 安全规范 目录 一 架构规范 所有的输入参数需要做合法性检验 xff1b 不允许出现空指针异常和数组越界异常 xff1b 不允许出现不受控制的大对象 xff0c 如没有限定大小的
  • 非主线程(GUI线程)中实现弹窗

    非主线程 xff08 GUI线程 xff09 中实现弹窗 目录一 在非主线程 xff08 GUI线程 xff09 中实现弹窗Step 1 创建界面文件 ui run loop dialog py Step 2 xff1a 代码主体 run
  • PyQT——多线程(QThread)

    PyQT线程 xff1a 多线程QThread 前言一 应用场景二 使用多线程解决卡顿和假死第一种 xff1a 线程锁 QMutex 第二种 xff1a 信号 Signal 前言 一 应用场景 在编写GUI界面中 xff0c 通常用会有一些
  • OpenCV-Python ——图像加文字

    Opencv 视频添加文字和标记框 前言一 OpenCV添加文字问题二 解决思路1 OpenCV图片转换为PIL图片格式2 使用PIL绘制文字3 PIL图片格式转换成OpenCV的图片格式 三 完整代码 前言 最近做了个视屏实时处理的需求
  • Django——工程搭建

    Django的工程搭建 目录 Django的工程搭建一 环境搭建1 创建虚拟环境2 安装Django3 虚拟环境和pip的命令 二 创建工程1 创建2 工程目录说明3 运行开发服务器 三 创建子应用1 创建2 子应用目录说明3 注册安装子应
  • Django——配置静态文件与路由

    静态文件与路由 一 配置文件1 BASE DIR2 DEBUG3 本地语言与时区 二 静态文件三 路由说明1 路由定义位置2 路由解析顺序3 路由命名与reverse反解析 xff08 逆向 xff09 3 1 路由命名3 2 revers
  • Django——请求与响应

    请求与响应 一 请求Request1 URL路径参数2 Django中的QueryDict对象3 查询字符串Query String4 请求体4 1 表单类型 Form Data4 2 非表单类型 Non Form Data 5 请求头6
  • 设计模式——单例模式/工厂模式

    设计模式 前言一 单例模式第 1 种 xff0c 重写 new 方法第 2 种 xff0c 闭包定义装饰器第 3 种 xff0c 多线程中构建单例 二 工厂模式1 简单工厂2 工厂方法3 抽象工厂 前言 常见的设计模式 xff0c 它们是
  • STM32 Futaba SBUS协议解析

    STM32 Futaba SBUS协议解析 1 S BUS1 1 协议介绍1 2 协议解析 2 硬件设计2 1 硬件参数2 2 反相电路 3 程序设计3 1 数据接收3 2 数据处理 4 最后 1 S BUS 1 1 协议介绍 S BUS是
  • Django——类视图

    类视图 前言1 类视图1 类视图引入2 类视图使用 2类视图原理3类视图使用装饰器3 1 在URL配置中装饰3 2 在类视图中装饰 4 类视图Mixin扩展类 前言 提示 xff1a 这里可以添加本文要记录的大概内容 xff1a 例如 xf
  • 设计模式——观察者模式

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 观察者模式 认识观察者模式观察者模式定义一 应用案例二 Python 实现总结 认识观察者模式 我们先看看报纸和杂志的订阅是怎么回事 xf
  • 面向对象设计原则

    面向对象设计原则是设计模式的基础 xff0c 每一个设计模式都符合某一个或者多个面向对象设计原则 xff0c 面向对象设计原则是用于评价一个设计模式的使用效果的重要指标之一 单一职责原则 一个对象应该只包含单一的职责 xff0c 并且该职责