SOLID 原则,程序设计五大原则,设计模式

SOLID 是让软件设计更易于理解、更加灵活和更易于维护的五个原则的简称。

  • 单一职责(Single Responsibility Principle):修改一个类的原因只能有一个。
  • 开闭原则(Open/Closed Principle):对于扩展,类应该是“开放”的;对于修改,类则应是“封闭”的。
  • 里氏替换原则(Liskov Substitution Principle):当你扩展一个类时, 记住你应该要能在不修改客户端代码的情况下将子类的对象作为父类对象进行传递。
  • 接口隔离原则(Interface Segregation Principle):客户端不应被强迫依赖于其不使用的方法。
  • 依赖倒置原则(Dependency Inversion Principle):高层次的类不应该依赖于低层次的类。 两者都应该依赖于抽象接口。抽象接口不应依赖于具体实现。具体实现应该依赖于抽象接口。

1.单一职责原则

修改一个类的原因只能有一个

尽量让每个类只负责软件中的一个功能,并将该功能完全封装在该类中。

这条原则的主要目的是减少复杂度。将原本复杂的业务拆分到若干个清晰地方法中去,避免费尽心思构思代码实现复杂设计。

如果类负责的东西太多,那么当其中任何一个功能发生改变时,你都必须对类进行修改。而在进行修改时,你就有可能改动类中自己并不希望改动的部分。

示例

2.开闭原则

对于扩展,类应该是“开放”的;对于修改,类则应是“封闭”的。

本原则地核心理念是在实现新功能时保持已有代码不变。

开放 :如果你可以对一个类进行扩展,可以创建它的子类并对其做任何事情(如新增方法或成员变量、重写基类行为等),那么它就是开放的。
封闭:如果某个类已经做好了充分的准备并可供其他类使用的话(即其接口已明确定义且以后不会修改),那么该类就是封闭(你可以称之为完整)的。

如果一个类已经完成开发、测试和审核工作,而且属于某个框架或者可被其他类的代码直接使用的话,对其代码进行修改就是有风险的。你可以创建一个子类并重写原始类的部分内容以完成不同的行为,而不是直接对原始类的代码进行修改。这样你既可以达成自己的目标,但同时又无需修改已有的原始类。

这条原则并不能应用于所有对类进行的修改中。如果你发现类中存在缺陷,直接对其进行修复即可,不要为它创建子类。子类不应该对其父类的问题负责。

示例

3.里氏替换原则

当你扩展一个类时, 记住你应该要能在不修改客户端代码的情况下将子类的对象作为父类对象进行传递。

这意味着子类必须保持与父类行为的兼容。在重写一个方法时,你要对基类行为进行扩展,而不是将其完全替换。

里氏替换原则由一组对子类形式的要求构成:

  • 子类方法的参数类型必须与其超类的参数类型相匹配或更加抽象

假设某个类有个方法用于给狗子喂食:feed(Dog g)。客户端代码总是会将“狗(dog)”对象传递给该方法。

  • 正确的子类:基于上述类创建了一个子类并重写了前面的方法,使其能够给任何“”“动物(animal,即‘狗’的超类)”喂食:feed(Animal d)。如果现在将该子类的对象而非超类的对象传递给客户端代码,程序仍能正常工作。
    该方法可以用于给传递给任何动物喂食,因此它仍然可以用于给传递给客户端的任何“狗”喂食。
  • 不正确的子类:基于上述类创建了另一个子类且限制喂食方法仅接受“哈士奇(Husky,一个‘狗’的子类)”:feed(Husky d)。如果你用他来替代链接在某个对象中的原始类,客户端代码会由于该方法只能对特殊种类的狗进行喂食,而无法为传递给客户端的普通狗提供服务,从而将破坏所有相关的功能。
  • 子类方法的返回值类型必须与超类方法的返回值类型或是其子类别相匹配

对于返回值类型的要求与对于参数类型的要求相反。
假设某个类中有一个方法 buyDog(): Dog 。 客户端代码执行该方法后的预期返回结果是任意类型的“狗”。

  • 正确的子类:子类将该方法重写为: buyDog(): Husky 。客户端将获得一只“哈士奇”,自然它也是一只“狗”,因此一切正常。
  • 不正确的子类:子类将该方法重写为: buyDog(): Animal 。现在客户端代码将会出错,因为它获得的是自己未知的动物种类(短吻鳄?熊?),不适用于为一只“狗”而设计的结构。
  • 子类中的方法不应抛出基础方法预期之外的异常类型

换句话说,异常类型必须与基础方法能抛出的异常或是其子类别相匹配。这条规则源于一个事实:客户端代码的 try-catch代码块针对的是基础方法可能抛出的异常类型。因此,预期之外的异常可能会穿透客户端的防御代码,从而使整个应用崩溃。

对于绝大部分现代编程语言, 特别是静态类型的编程语言(Java 和 C# 等等),已经内置上述三条规则。如果违反了这些规则,你将无法对程序进行编译。

  • 子类不应该加强其前置条件

例如,基类的方法有一个 int类型的参数。如果子类重写该方法时,要求传递给该方法的参数值必须为正数(如果该值为负则抛出异常),这就是加强了前置条件。客户端代码之前将负数传递给该方法时程序能够正常运行,但现在使用子类的对象时会使程序出错。

  • 子类不能削弱其后置条件

假如你的某个类中有个方法需要使用数据库,该方法应该在接收到返回值后关闭所有活跃的数据库连接。你创建了一个子类并对其进行了修改,使得数据库保持连接以便重用。但客户端可能对你的意图一无所知。由于它认为该方法会关闭所有的连接,因此可能会在调用该方法后就马上关闭程序,使得无用的数据库连接对系统造成“污染”。

  • 超类的不变量必须保留

这很可能是所有规则中最不“形式”的一条。不变量是让对象有意义的条件。例如,猫的不变量是有四条腿、一条尾巴和能够喵喵叫等。不变量让人疑惑的地方在于它们既可通过接口契约或方法内的一组断言来明确定义,又可暗含在特定的单元测试和客户代码预期中。不变量的规则是最容易违反的,因为你可能会误解或没有意识到一个复杂类中的所有不变量。因此,扩展一个类的最安全做法是引入新的成员变量和方法,而不要去招惹超类中已有的成员。当然在实际中,这并非总是可行。

  • 子类不能修改超类中私有成员变量的值

什么?这难道可能吗?原来有些编程语言允许通过反射机制来访问类的私有成员。还有一些语言(Python 和 JavaScript)没有对私有成员进行任何保护。

示例

接口隔离原则

客户端不应被强迫依赖于其不使用的方法

尽量缩小接口的范围,使得客户端的类不实现其不需要的行为。

根据接口隔离原则,你必须将“臃肿”的方法拆分为多个颗粒度更小的具体方法。客户端必须仅实现其实际需要的方法。否则,对于“臃肿”接口的修改可能会导致程序出错,即使客户端根本没有使用修改后的方法。

继承只允许类拥有一个超类,但是它并不限制类可同时实现的接口的数量。因此,你不需要将大量无关的类塞进单个接口。你可将其拆分为更精细的接口,如有需要可在单个类中实现所有接口,某些类也可只实现其中的一个接口。

示例

依赖倒置原则

高层次的类不应该依赖于低层次的类。 两者都应该依赖于抽象接口。抽象接口不应依赖于具体实现。具体实现应该依赖于抽象接口。

  • 低层次的类:实现基础操作(例如磁盘操作、传输网络数据和连接数据库等)。
  • 给层次的类:包含复杂业务逻辑以指导低层次类执行特定操作。

有时人们会先设计低层次的类,然后才会开发高层次的类。当你在新系统上开发原型产品时,这种情况很常见。由于低层次的东西还没有实现或不确定,你甚至无法确定高层次类能实现哪些功能。如果采用这种方式,业务逻辑类可能会更依赖于低层原语类。

依赖倒置原则建议改变这种依赖方式。

  • 1.使用业务术语来对高层次类依赖的低层次操作接口进行描述。例如, 业务逻辑应该调用名为 openReport(file) 的 方 法, 而 不 是 openFile(x) 、readBytes(n) 和 closeFile(x) 等一系列方法。 这些接口被视为是高层次的。
  • 2.基于这些接口创建高层次类,而不是基于低层次的具体类。这要比原始的依赖关系灵活很多。
  • 3.一旦低层次的类实现了这些接口,它们将依赖于业务逻辑层,从而倒置了原始的依赖关系。

依赖倒置原则通常和开闭原则共同发挥作用:你无需修改已有类就能用不同的业务逻辑类扩展低层次的类。

示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/140027.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

组件的设计原则

目录 插槽的基本概念 基础用法 具名插槽 使用场景 布局控制 嵌套组件 组件的灵活性 高级用法 作用域插槽 总结 前言 Vue 的 slot 是一项强大的特性,用于组件化开发中。它允许父组件向子组件传递内容,使得组件更加灵活和可复用。通过 slot&…

Python之函数进阶-nonlocal和LEGB

Python之函数进阶-nonlocal和LEGB nonlocal语句 nonlocal:将变量标记为不在本地作用域定义,而是在上级的某一级局部作用域中定义,但不能是全局作用域中定义。 函数的销毁 定义一个函数就是生成一个函数对象,函数名指向的就是函数对象。可…

在 React Router 中使用 JWT

在这篇文章中,我们将探讨 JWT 身份校验与 React 和 React-router 的无缝集成。 我们还将学习如何处理公共路由、受校验保护路由,以及如何利用 axios 库通过身份验证令牌(token)发出 API 请求。 创建一个 React 项目 使用下方的指…

华为云Ascend310服务器使用

使用华为云服务器 cpu: 16vCPUs Kunpeng 920 内存:16GiB gpu:4* HUAWEI Ascend 310 cann: 20.1.rc1 操作系统:Ubuntu aarch64目的 使用该服务器进行docker镜像编译,测试模型。 已知生产环境:mindx版本为3.0.rc3&a…

keep-alive缓存,三级路由不生效

此文章讲诉在vue中使用keep-alive缓存,三级路由缓存失败处理方案。 一二级路由缓存无任何问题,三级以上就会失败,因此我们在路由守卫中对matched做出如下优化 Router.beforeEach((to, from, next)>{if(to.matched && to.matched.l…

【机器学习】Kmeans聚类算法

一、聚类简介 Clustering (聚类)是常见的unsupervised learning (无监督学习)方法,简单地说就是把相似的数据样本分到一组(簇),聚类的过程,我们并不清楚某一类是什么(通常无标签信息)&#xff0…

【PTE-day07 文件上传2】

1、常见的绕过方式 (1)畸形后缀名绕过 .php、.pht、.php3、.php4、.php5、.php2、.phtml、.pHp、.html、.Htm......(2)双写过滤字符绕过 (3).htaccess文件绕过 <FilesMatch "jpg"> SetHandler application/x-httpd-php

通义千问, 文心一言, ChatGLM, GPT-4, Llama2, DevOps 能力评测

引言 “克隆 dev 环境到 test 环境&#xff0c;等所有服务运行正常之后&#xff0c;把访问地址告诉我”&#xff0c;“检查所有项目&#xff0c;告诉我有哪些服务不正常&#xff0c;给出异常原因和修复建议”&#xff0c;在过去的工程师生涯中&#xff0c;也曾幻想过能够通过这…

目标检测工程化最佳实践:Python 并行条件下YOLOv8的模型推理,线程安全的模型推理!

文章大纲 YOLOv8模型的线程安全推理 背景简介Python 线程的一些理解共享模型实例的问题与危害!非线程安全的代码样例: 单个模型实例非线程安全的代码样例: 多个个模型实例YOLOv8 中线程安全的推理方式Thread-Safe Example 1Thread-Safe Example 2YOLOv8 主要开发人员的回复结论…

【FAQ】Gradle开发问题汇总

1. buildSrc依赖Spring Denpendency时报错 来自预编译脚本的插件请求不能包含版本号。请从有问题的请求中删除该版本&#xff0c;并确保包含所请求插件io.spring.dependency-management的模块是一个实现依赖项 解决方案 https://www.5axxw.com/questions/content/uqw0grhttps:/…

Flink之Catalog

Catalog Catalog概述Catalog分类 GenericInMemoryCatalogJdbcCatalog下载JAR包及使用重启操作创建Catalog查看与使用Catalog自动初始化catalog HiveCatalog下载JAR包及使用重启操作hive metastore服务创建Catalog查看与使用CatalogFlink与Hive中操作自动初始化catalog 用户自定…

基于springboot实现桥牌计分管理系统项目【项目源码】计算机毕业设计

基于springboot实现桥牌计分管理系统演示 JAVA简介 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#…

MYSQL操作详解

一)计算机的基本结构 但是实际上&#xff0c;更多的是这种情况: 二)MYSQL中的数据类型: 一)数值类型: 数据类型内存大小(字节)说明bit(M)M指定位数,默认为1单个二进制位值&#xff0c;或者为0或者为1&#xff0c;主要用于开/关标志tinyint1字节1个字节的整数值&#xff0c;支持…

KITTI数据集(.bin数据)转换为点云数据(.pcd文件)

目录 cmake代码 代码 cmake代码 cmake_minimum_required(VERSION 3.17) project(TEST2)set(CMAKE_CXX_STANDARD 14)# Find PCL find_package(PCL 1.8 REQUIRED)# If PCL was found, add its include directories to the project if(PCL_FOUND)include_directories(${PCL_INC…

使用openvc进行人脸检测:Haar级联分类器

1 人脸检测介绍 1.1 什么是人脸检测 人脸检测的目标是确定图像或视频中是否存在人脸。如果存在多个面&#xff0c;则每个面都被一个边界框包围&#xff0c;因此我们知道这些面的位置 人脸检测算法的主要目标是准确有效地确定图像或视频中人脸的存在和位置。这些算法分析数据…

Unity 获取对象的方法

Unity获取对象的方法还是有不少的。 以下是一些我知道的方法。 1、GameObject.Find()&#xff1a;这是一种最简单的方法&#xff0c;可以通过对象的名称来查找对象。例如&#xff0c;GameObject.Find("Cube")&#xff1b;将返回名称为"Cube"的对象。 2、…

一文入门Springboot+actuator+Prometheus+Grafana

环境介绍 技术栈 springbootmybatis-plusmysqloracleactuatorPrometheusGrafana 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 本地主机应用 192.168.1.9:8007 PrometheusGrafana安装在同一台主机 http://…

[西湖论剑 2022]real_ez_node

文章目录 前置知识EJS模板注入&#xff08;CVE-2022-29078&#xff09;原型链污染漏洞 &#xff08;CVE-2021-25928&#xff09;HTTP响应拆分攻击&#xff08;CRLF&#xff09; 解题过程代码审计构造payload 前置知识 EJS模板注入&#xff08;CVE-2022-29078&#xff09; EJS…

.net在使用存储过程中IN参数的拼接方案,使用Join()方法

有时候拼接SQL语句时&#xff0c;可能会需要将list中的元素都加上单引号&#xff0c;并以逗号分开&#xff0c;但是Join只能简单的分开&#xff0c;没有有单引号&#xff01; 1.第一种拼接方案 List<string> arrIds new List<string>(); arrIds.Add("aa&qu…

Cross-Origin跨站问题详解(跨站请求、跨站cookie)

背景&#xff1a;我部署frontend和backend到两个不同的docker容器&#xff0c;前端路径为http://localhost:3000&#xff0c;后端路径为http://localhost:4000。我设置了用户登录功能&#xff0c;并使用cookie进行session管理。当我的前端登录时&#xff0c;创建了一个session&…