当前位置: 首页 > news >正文

【设计模式之美 设计原则与思想:设计原则】20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?

上几节课中,我们学习了经典的 SOLID 原则。今天,我们讲两个设计原则:KISS 原则和 YAGNI 原则。其中,KISS 原则比较经典,耳熟能详,但 YAGNI 你可能没怎么听过,不过它理解起来也不难。

理解这两个原则时候,经常会有一个共同的问题,那就是,看一眼就感觉懂了,但深究的话,又有很多细节问题不是很清楚。比如,怎么理解 KISS 原则中“简单”两个字?什么样的代码才算“简单”?怎样的代码才算“复杂”?如何才能写出“简单”的代码?YAGNI 原则跟 KISS 原则说的是一回事吗?

如果你还不能非常清晰地回答出上面这几个问题,那恭喜你,又得到了一次进步提高的机会。等你听完这节课,我相信你很自然就能回答上来了。话不多说,让我们带着这些问题,正式开始今天的学习吧!

如何理解“KISS 原则”?

KISS 原则的英文描述有好几个版本,比如下面这几个。

  • Keep It Simple and Stupid.
  • Keep It Short and Simple.
  • Keep It Simple and Straightforward.

不过,仔细看你就会发现,它们要表达的意思其实差不多,翻译成中文就是:尽量保持简单。

KISS 原则算是一个万金油类型的设计原则,可以应用在很多场景中。它不仅经常用来指导软件开发,还经常用来指导更加广泛的系统设计、产品设计等,比如,冰箱、建筑、iPhone 手机的设计等等。不过,咱们的专栏是讲代码设计的,所以,接下来,我还是重点讲解如何在编码开发中应用这条原则。

我们知道,代码的可读性和可维护性是衡量代码质量非常重要的两个标准。而 KISS 原则就是保持代码可读和可维护的重要手段。代码足够简单,也就意味着很容易读懂,bug 比较难隐藏。即便出现 bug,修复起来也比较简单。

不过,这条原则只是告诉我们,要保持代码“Simple and Stupid”,但并没有讲到,什么样的代码才是“Simple and Stupid”的,更没有给出特别明确的方法论,来指导如何开发出“Simple and Stupid”的代码。所以,看着非常简单,但不能落地,这就有点像我们常说的“心灵鸡汤”。哦,咱们这里应该叫“技术鸡汤”。

所以,接下来,为了能让这条原则切实地落地,能够指导实际的项目开发,我就针对刚刚的这些问题来进一步讲讲我的理解。

代码行数越少就越“简单”吗?

我们先一起看一个例子。下面这三段代码可以实现同样一个功能:检查输入的字符串 ipAddress 是否是合法的 IP 地址。

一个合法的 IP 地址由四个数字组成,并且通过“.”来进行分割。每组数字的取值范围是 0~255。第一组数字比较特殊,不允许为 0。对比这三段代码,你觉得哪一段代码最符合 KISS 原则呢?如果让你来实现这个功能,你会选择用哪种实现方法呢?你可以先自己思考一下,然后再看我下面的讲解。

// 第一种实现方式: 使用正则表达式
public boolean isValidIpAddressV1(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";return ipAddress.matches(regex);
}
// 第二种实现方式: 使用现成的工具类
public boolean isValidIpAddressV2(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String[] ipUnits = StringUtils.split(ipAddress, '.');if (ipUnits.length != 4) {return false;}for (int i = 0; i < 4; ++i) {int ipUnitIntValue;try {ipUnitIntValue = Integer.parseInt(ipUnits[i]);} catch (NumberFormatException e) {return false;}if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {return false;}if (i == 0 && ipUnitIntValue == 0) {return false;}}return true;
}
// 第三种实现方式: 不使用任何工具类
public boolean isValidIpAddressV3(String ipAddress) {char[] ipChars = ipAddress.toCharArray();int length = ipChars.length;int ipUnitIntValue = -1;boolean isFirstUnit = true;int unitsCount = 0;for (int i = 0; i < length; ++i) {char c = ipChars[i];if (c == '.') {if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;if (isFirstUnit && ipUnitIntValue == 0) return false;if (isFirstUnit) isFirstUnit = false;ipUnitIntValue = -1;unitsCount++;continue;}if (c < '0' || c > '9') {return false;}if (ipUnitIntValue == -1) ipUnitIntValue = 0;ipUnitIntValue = ipUnitIntValue * 10 + (c - '0');}if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;if (unitsCount != 3) return false;return true;
}

第一种实现方式利用的是正则表达式,只用三行代码就把这个问题搞定了。它的代码行数最少,那是不是就最符合 KISS 原则呢?答案是否定的。虽然代码行数最少,看似最简单,实际上却很复杂。这正是因为它使用了正则表达式。

一方面,正则表达式本身是比较复杂的,写出完全没有 bug 的正则表达本身就比较有挑战;另一方面,并不是每个程序员都精通正则表达式。对于不怎么懂正则表达式的同事来说,看懂并且维护这段正则表达式是比较困难的。这种实现方式会导致代码的可读性和可维护性变差,所以,从 KISS 原则的设计初衷上来讲,这种实现方式并不符合 KISS 原则。

讲完了第一种实现方式,我们再来看下其他两种实现方式。

第二种实现方式使用了 StringUtils 类、Integer 类提供的一些现成的工具函数,来处理 IP 地址字符串。第三种实现方式,不使用任何工具函数,而是通过逐一处理 IP 地址中的字符,来判断是否合法。从代码行数上来说,这两种方式差不多。但是,第三种要比第二种更加有难度,更容易写出 bug。从可读性上来说,第二种实现方式的代码逻辑更清晰、更好理解。所以,在这两种实现方式中,第二种实现方式更加“简单”,更加符合 KISS 原则。

不过,你可能会说,第三种实现方式虽然实现起来稍微有点复杂,但性能要比第二种实现方式高一些啊。从性能的角度来说,选择第三种实现方式是不是更好些呢?

在回答这个问题之前,我先解释一下,为什么说第三种实现方式性能会更高一些。一般来说,工具类的功能都比较通用和全面,所以,在代码实现上,需要考虑和处理更多的细节,执行效率就会有所影响。而第三种实现方式,完全是自己操作底层字符,只针对 IP 地址这一种格式的数据输入来做处理,没有太多多余的函数调用和其他不必要的处理逻辑,所以,在执行效率上,这种类似定制化的处理代码方式肯定比通用的工具类要高些。

不过,尽管第三种实现方式性能更高些,但我还是更倾向于选择第二种实现方法。那是因为第三种实现方式实际上是一种过度优化。除非 isValidIpAddress() 函数是影响系统性能的瓶颈代码,否则,这样优化的投入产出比并不高,增加了代码实现的难度、牺牲了代码的可读性,性能上的提升却并不明显。

代码逻辑复杂就违背 KISS 原则吗?

刚刚我们提到,并不是代码行数越少就越“简单”,还要考虑逻辑复杂度、实现难度、代码的可读性等。那如果一段代码的逻辑复杂、实现难度大、可读性也不太好,是不是就一定违背 KISS 原则呢?在回答这个问题之前,我们先来看下面这段代码:

// KMP algorithm: a, b 分别是主串和模式串;n, m 分别是主串和模式串的长度。
public static int kmp(char[] a, int n, char[] b, int m) {int[] next = getNexts(b, m);int j = 0;for (int i = 0; i < n; ++i) {while (j > 0 && a[i] != b[j]) { // 一直找到 a[i] 和 b[j]j = next[j - 1] + 1;}if (a[i] == b[j]) {++j;}if (j == m) { // 找到匹配模式串的了return i - m + 1;}}return -1;
}
// b 表示模式串,m 表示模式串的长度
private static int[] getNexts(char[] b, int m) {int[] next = new int[m];next[0] = -1;int k = -1;for (int i = 1; i < m; ++i) {while (k != -1 && b[k + 1] != b[i]) {k = next[k];}if (b[k + 1] == b[i]) {++k;}next[i] = k;}return next;
}

这段代码来自我的另一个专栏《数据结构与算法之美》中KMP 字符串匹配算法的代码实现。这段代码完全符合我们刚提到的逻辑复杂、实现难度大、可读性差的特点,但它并不违反 KISS 原则。为什么这么说呢?

KMP 算法以快速高效著称。当我们需要处理长文本字符串匹配问题(几百 MB 大小文本内容的匹配),或者字符串匹配是某个产品的核心功能(比如 Vim、Word 等文本编辑器),又或者字符串匹配算法是系统性能瓶颈的时候,我们就应该选择尽可能高效的 KMP 算法。而 KMP 算法本身具有逻辑复杂、实现难度大、可读性差的特点。本身就复杂的问题,用复杂的方法解决,并不违背 KISS 原则。

不过,平时的项目开发中涉及的字符串匹配问题,大部分都是针对比较小的文本。在这种情况下,直接调用编程语言提供的现成的字符串匹配函数就足够了。如果非得用 KMP 算法、BM 算法来实现字符串匹配,那就真的违背 KISS 原则了。也就是说,同样的代码,在某个业务场景下满足 KISS 原则,换一个应用场景可能就不满足了。

如何写出满足 KISS 原则的代码?

实际上,我们前面已经讲到了一些方法。这里我稍微总结一下。

  • 不要使用同事可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等。
  • 不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出 bug 的概率会更高,维护的成本也比较高。
  • 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替 if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。

实际上,代码是否足够简单是一个挺主观的评判。同样的代码,有的人觉得简单,有的人觉得不够简单。而往往自己编写的代码,自己都会觉得够简单。所以,评判代码是否简单,还有一个很有效的间接方法,那就是 code review。如果在 code review 的时候,同事对你的代码有很多疑问,那就说明你的代码有可能不够“简单”,需要优化啦。

这里我还想多说两句,我们在做开发的时候,一定不要过度设计,不要觉得简单的东西就没有技术含量。实际上,越是能用简单的方法解决复杂的问题,越能体现一个人的能力。

YAGNI 跟 KISS 说的是一回事吗?

YAGNI 原则的英文全称是:You Ain’t Gonna Need It。直译就是:你不会需要它。这条原则也算是万金油了。当用在软件开发中的时候,它的意思是:不要去设计当前用不到的功能;不要去编写当前用不到的代码。实际上,这条原则的核心思想就是:不要做过度设计。

比如,我们的系统暂时只用 Redis 存储配置信息,以后可能会用到 ZooKeeper。根据 YAGNI 原则,在未用到 ZooKeeper 之前,我们没必要提前编写这部分代码。当然,这并不是说我们就不需要考虑代码的扩展性。我们还是要预留好扩展点,等到需要的时候,再去实现 ZooKeeper 存储配置信息这部分代码。

再比如,我们不要在项目中提前引入不需要依赖的开发包。对于 Java 程序员来说,我们经常使用 Maven 或者 Gradle 来管理依赖的类库(library)。我发现,有些同事为了避免开发中 library 包缺失而频繁地修改 Maven 或者 Gradle 配置文件,提前往项目里引入大量常用的 library 包。实际上,这样的做法也是违背 YAGNI 原则的。

从刚刚的分析我们可以看出,YAGNI 原则跟 KISS 原则并非一回事儿。KISS 原则讲的是“如何做”的问题(尽量保持简单),而 YAGNI 原则说的是“要不要做”的问题(当前不需要的就不要做)。

重点回顾

好了,今天的内容到此就讲完了。我们现在来总结回顾一下,你需要掌握的重点内容。

KISS 原则是保持代码可读和可维护的重要手段。KISS 原则中的“简单”并不是以代码行数来考量的。代码行数越少并不代表代码越简单,我们还要考虑逻辑复杂度、实现难度、代码的可读性等。而且,本身就复杂的问题,用复杂的方法解决,并不违背 KISS 原则。除此之外,同样的代码,在某个业务场景下满足 KISS 原则,换一个应用场景可能就不满足了。

对于如何写出满足 KISS 原则的代码,我还总结了下面几条指导原则:

  • 不要使用同事可能不懂的技术来实现代码;
  • 不要重复造轮子,要善于使用已经有的工具类库;
  • 不要过度优化。

课堂讨论

你怎么看待在开发中重复造轮子这件事情?什么时候要重复造轮子?什么时候应该使用现成的工具类库、开源框架?

欢迎在留言区写下你的答案,和同学一起交流和分享。如果有收获,也欢迎你把这篇文章分享给你的朋友。

相关文章:

【设计模式之美 设计原则与思想:设计原则】20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?

上几节课中&#xff0c;我们学习了经典的 SOLID 原则。今天&#xff0c;我们讲两个设计原则&#xff1a;KISS 原则和 YAGNI 原则。其中&#xff0c;KISS 原则比较经典&#xff0c;耳熟能详&#xff0c;但 YAGNI 你可能没怎么听过&#xff0c;不过它理解起来也不难。 理解这两个…...

Java代码弱点与修复之——Copy-paste error(复制粘贴错误)

弱点描述 Copy-paste error,复制粘贴错误。 是指在复制和粘贴代码时产生的错误。这种错误通常是由于程序员在复制代码时未正确编辑所复制的代码或编辑复制后的代码时忘记更改一些值或参数而导致的。复制粘贴错误可能会导致程序逻辑错误、编译错误或运行时错误。 示例代码 …...

Editor.md 的使用方法及图片处理

目录1. 资源下载2. 生成页面2.1 编辑和预览页面2.2 文本渲染页面3. 图片上传3.1 前端配置3.2 后端接口4. 图片粘贴[^2]1. 资源下载 官网下载 gitee 下载 2. 生成页面 2.1 编辑和预览页面 将资源&#xff08;精简后 Editor.md 资源1&#xff09;导入项目&#xff1a; 按照官…...

剑指 Offer II 018. 有效的回文

题目链接 剑指 Offer II 018. 有效的回文 easy 题目描述 给定一个字符串 s&#xff0c;验证 s是否是 回文串 &#xff0c;只考虑字母和数字字符&#xff0c;可以忽略字母的大小写。 本题中&#xff0c;将空字符串定义为有效的 回文串 。 示例 1: 输入: s “A man, a plan, …...

Elasticsearch分析器(Analyzer)

Elasticsearch分析器&#xff08;Analyzer&#xff09; 文章目录Elasticsearch分析器&#xff08;Analyzer&#xff09;分析器概念内置分析器&#xff08;8.6版本&#xff09;自定义分析器elasticsearch-analysis-ik&#xff08;简称ik&#xff0c;&#x1f495;14.8k&#xff…...

P6入门:了解P6 Professional 工具栏及地图分享

目录 引言 相关分享 引言 凭借更大的灵活性和增强的自定义功能&#xff0c;最新版本的 Oracle Primavera P6 Professional 的界面比早期版本有了巨大改进。对于有经验的伙伴来说&#xff0c;它仍然是熟悉的领域&#xff0c;几乎所有预期的功能都显示在前面。该界面可以更好地…...

习题30 if elif else 语句

people 30#变量people赋值30 cars 40#变量cars赋值40 buses 15#变量buses赋值 if cars > people:#如果出租车比人多print("We should take the cars")#我们坐出租车 elif cars < people:#elif后面必须跟条件&#xff0c;print("We should not take the…...

32 openEuler使用LVM管理硬盘-管理卷组

文章目录32 openEuler使用LVM管理硬盘-管理卷组32.1 创建卷组32.2 查看卷组32.3 修改卷组属性32.4 扩展卷组32.5 收缩卷组32.6 删除卷组32 openEuler使用LVM管理硬盘-管理卷组 32.1 创建卷组 可在root权限下通过vgcreate命令创建卷组。 vgcreate [option] vgname pvname ...…...

Jackson CVE-2017-17485 反序列化漏洞

0x00 前言 同CVE-2017-15095一样&#xff0c;是CVE-2017-7525黑名单绕过的漏洞&#xff0c;主要还是看一下绕过的调用链利用方式。 可以先看&#xff1a; Jackson 反序列化漏洞原理 或者直接看总结也可以&#xff1a; Jackson总结 涉及版本&#xff1a;2.8.10和2.9.x至2.…...

十大排序(C++版)

测试排序的题目&#xff1a; 912. 排序数组 - 力扣&#xff08;LeetCode&#xff09; 堕落的做法&#xff1a; class Solution { public:vector<int> sortArray(vector<int>& nums) {sort(nums.begin(),nums.end());return nums;} };视频推荐&#xff1a; …...

SpringMVC中的常用注解

Java知识点总结&#xff1a;想看的可以从这里进入 目录3.2、常用的注解3.2、常用的注解 Controller&#xff1a;代表此类是一个控制器&#xff0c;需要配置包的扫描。Spring MVC 是通过组件扫描机制查找应用中的控制器类的 在Spring6.0之后要求控制层必须添加该注解才会被识别成…...

English Learning - L2-3 英音地道语音语调 小元音 [ʌ] [ɒ] [ʊ] [ɪ] [ə] [e] 2023.02.27 周一

English Learning - L2-3 英音地道语音语调 小元音 [ʌ] [ɒ] [ʊ] [ɪ] [ə] [e] 2023.02.27 周一课前活动练习方法大小元音总结小元音准备工作[ʌ] 中元音发音技巧对应单词的发音对应句子的发音常见的字母组合[ɒ] 后元音发音技巧对应单词的发音对应句子的发音常见的字母组合…...

fastadmin后台登录页修改

直接替换就行 <!DOCTYPE html> <html lang"{$config.language}"> <head>{include file"common/meta" /}<style type"text/css">body {color: #999;background-color: #f1f4fd;background-size: cover;}a {color: #444;…...

Java 面向对象(OOP)的三大特性

封装 所谓封装&#xff0c;意思就是隐藏内部细节&#xff0c;在编程中&#xff0c;指利用抽象数据类型将数据和基于数据的操作封装在一起&#xff0c;使其构成一个不可分割的独立实体&#xff0c;并尽可能地隐藏内部的细节&#xff0c;只保留一些对外接口使之与外部发生联系。…...

Java:openjdk: error: Student is abstract; cannot be instantiated;java编译环境

文章目录编译环境jdkopenjdk错误代码小心javac -verbos编译环境 jdk 需要安装的javac 在java-devel 包里 [root10 ~]# rpm -qf /usr/bin/javac file /usr/bin/javac is not owned by any package [root10 ~]# ll /usr/bin/javac lrwxrwxrwx. 1 root root 23 Jun 15 09:52 /us…...

28个案例问题分析---019---临时解决方案和最终解决方案--思想

临时解决方案与最终解决方案一&#xff1a;背景介绍二&#xff1a;临时解决方案&#xff1f;最终解决方案&#xff1f;概念如何选择三&#xff1a;总结一&#xff1a;背景介绍 项目中&#xff0c;出现了一个线上问题。 用户登陆之后看不到课程。重新登陆就可以看到课程。出现这…...

计算机网络的166个概念你知道几个 第四部分

HTML&#xff1a;HTML 称为超文本标记语言&#xff0c;是一种标识性的语言。它包括一系列标签&#xff0e;通过这些标签可以将网络上的文档格式统一&#xff0c;使分散的 Internet 资源连接为一个逻辑整体。HTML 文本是由 HTML 命令组成的描述性文本&#xff0c;HTML 命令可以说…...

Lenovo 联想-IdeaPad-Y530电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。硬件型号驱动情况主板联想-IdeaPad-Y530处理器Intel 酷睿2双核 T9400已驱动内存2GB已驱动硬盘2TB HP EX950 PCI-E Gen3 x4 NVMe SSD已驱动显卡NVIDIA GeForce 9300M GS无法驱动声卡Realtek ALC888无法驱动网卡RTL8168H Giga…...

mac M1 nvm安装教程,避坑

mac M1 nvm 安装问题 新款的mac搭载了苹果自研的芯片&#xff0c;放弃了intel的x86芯片&#xff0c;那之前的软件难免会存在兼容性问题。 鄙人有幸踩了第一个坑。 在通过nvm 安装不同版本的node 时&#xff0c;出现了问题。 问题一&#xff1a;先说一下 nvm的安装问题&#…...

【项目精选】基于网络爬虫技术的网络新闻分析(视频+论文+源码)

点击下载源码 基于网络爬虫技术的网络新闻分析主要用于网络数据爬取。本系统结构如下&#xff1a; &#xff08;1&#xff09;网络爬虫模块。 &#xff08;2&#xff09;中文分词模块。 &#xff08;3&#xff09;中3文相似度判定模块。 &#xff08;4&#xff09;数据结构化存…...

【Python - Matplotlib】P2 plot 折线图

Matplotlib绘制折线图折线图完整代码与效果基础折线图设定横纵坐标设置中文显示添加网格添加描述信息再添加一个城市设置两个折线图前言 上一节内容主要围绕介绍 Matplotlib 的画板结构。 链接&#xff1a;https://blog.csdn.net/weixin_43098506/article/details/129331576 本…...

【Verilog】——模块,常量,变量

目录 1.模块 1.描述电路的逻辑功能 2. 门级描述 3.模块的模板​编辑 2.关键字 3.标识符 4.Verilog源代码的编写标准 5.数据类型 1.整数常量​ 2.参数传递的两种方法 3.变量 4.reg和wire的区别 5.沿触发和电平触发的区别​ 6.memory型变脸和reg型变量的区别​ 1.模块 1.描…...

论文投稿指南——中文核心期刊推荐(电影、电视艺术)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…...

Pip install 和Conda install 总结

版本一 conda install xxx&#xff1a;这种方式安装的库都会放在/Users/orion-orion/miniforge3/pkgs目录下。我们在我们的虚拟环境中要用到或下载时先到该路径下去找&#xff0c;若有则直接将其复制到我们的虚拟环境中包得存放位置: ~/site-packages/。若没有&#xff0c;则先…...

嵌入式系统实验——【玄武F103开发板】实现两个LED小灯闪烁

目录一、实验文件main.cstm32f10x.h二、实验思路&#xff08;一&#xff09;打开两个LED小灯1.在玄武F103开发板上找到LED0、LED1对应的GPIO控制寄存器2.找到GPIOB、GPIOE的地址3.打开APB2外设时钟的使能寄存器4.对GPIO寄存器进行设置对端口输出寄存器进行设置&#xff08;二&a…...

数组之双指针题

文章目录一、最长连续不重复子序列1.题目介绍2.思路3.二、长度最小的子数组1.题目介绍2.思路3.代码三、数组元素的目标和1.题目介绍2.思路3.代码总结其实在之前我写过不少双指针得题解&#xff0c;刷题专练之数组移除元素 刷题专练之翻转题练习这两篇文章的题解基本就是双指针法…...

真实需求和梦想实现满足

多少的时光和岁月中都不曾认真系统的深度思考自己的真实需求和欲望之间是否一致&#xff0c;随着时间的流逝才发现自己追求的是一场空&#xff0c;自己的真实需求并不是苦苦追求的东西&#xff0c;这也是当梦想照进现实&#xff01;欲望是无善无恶的&#xff0c;不必为了满足自…...

[ant-design-vue] tree 组件功能使用

[ant-design-vue] tree 组件功能使用描述环境信息相关代码参数说明描述 是希望展现一个树形的菜单&#xff0c;并且对应的菜单前有复选框功能&#xff0c;但是对比官网的例子&#xff0c;我们在使用的过程中涉及到对半选中情况的处理&#xff1a; 半选中状态&#xff1a; 选中…...

QT父子窗口事件传递与事件过滤器

处理监控系统的时候遇到问题&#xff0c;在MainWidget中创建多个子Widget的时候&#xff0c;原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理&#xff0c;但调试后发现如果子Widget重新实现了事件方法&#xff0c;就直接处理掉事件了&#xff0c;没有进到Main…...

【2.4 golang中的循环语句for】

- 循环语句for 1. 循环语句for 1.1.1. Golang for支持三种循环方式&#xff0c;包括类似 while 的语法。 for循环是一个循环控制结构&#xff0c;可以执行指定次数的循环。 语法 Go语言的For循环有3中形式&#xff0c;只有其中的一种使用分号。 for init; condition; pos…...