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

读程序员的制胜技笔记04_有用的反模式(下)

1. 重新发明轮子

1.1. 发明家的特质就是要用质疑的心态对待所有事物,你从未停下质疑,那你将不可避免地成为一个发明家

1.2. 并非所有的事情都有现成的轮子可以拿来用

1.3. 自己重新写一个新的API,最终调用你使用的库

1.3.1. 你的API应该是极简的,满足你的需求就可以了

1.3.1.1. 自己做自己的甲方

1.3.2. 拥有你自己的支持适配器的方便接口的方法在业界被称为适配器模式(adapter pattern)

2. SOLID原则

2.1. S:单一责任原则(single-responsibility principle)

2.1.1. 一个类应该只负责一件事

2.1.2. 不是一个类做多件事,也就是God类

2.1.3. 一个类的名字应该尽可能简洁地解释它的功能,而不是含糊不清

2.1.4. 如果名字太长或太模糊,就需要将该类拆成多个类

2.2. O:开放-封闭原则(open-closed principle)

2.2.1. 一个类应该为扩展而开放,但为修改而封闭

2.2.2. 类设计成其行为可以被外部修改

2.2.3. 可扩展性是一个设计决定,有时可能并不可取、不实用,甚至不安全

2.3. L:由芭芭拉·利斯科夫(Barbara Liskov)提出的里氏替换原则(Liskov Substitution principle)

2.3.1. 程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的

2.4. I:接口隔离原则(interface segregation principle)

2.4.1. 偏向于较小的、目标明确的接口,而不是泛化的、范围广泛的接口

2.4.2. 这是一个不必要的复杂和模糊的建议,甚至可以说是完全错误的

2.4.3. 分割接口不是基于范围,而是基于设计的实际需求

2.4.4. 如果单一接口不适合这项工作,请随意拆分它,而不是为了刻意满足某些定死的僵硬教条

2.5. D:依赖反转原则(dependency inversion principle)

2.5.1. 代码不应该依赖具体的实现,而应该依赖抽象

2.5.2. 继承将你绑定在一个具体的实现上,违反了该原则

2.5.3. 当你喜欢灵活性并且看到它的价值时,你更喜欢依赖抽象,而在无关紧要的情况下,你会依赖具体实现

3. 不要使用继承

3.1. 面向对象编程(Object-Oriented Programming,OOP)最强调的特点是继承

3.1.1. 在常规的继承中,普通代码和它的变化之间的关系是用父类/子类(ancestor/ descendant)模型来表达的

3.2. 从长远来看,继承带来的问题比它解决的问题要多

3.2.1. 多重继承(multiple inheritance)是首要问题之一

3.2.2. 除了多重继承之外,继承的一个更大的问题是强依赖性

3.2.2.1. 紧耦合(tight coupling)
3.2.2.2. 依赖性是万恶之源

3.3. 组合(composition)

3.3.1. 你不是从类中继承,而是在你的构造函数中接收它的抽象作为参数

3.3.2. 把你的组件看成相互拼接的乐高积木块,而不是对象的层次结构

3.3.3. 组合把共同的功能看成独立的组件

3.3.4. 组合更像是一种客户端-服务器的关系,而不是父-子关系

3.3.5. 通过它的引用来调用复用的代码,而不是在你的范围内继承它的方法

3.3.6. 把类作为参数进行接收还有一个额外的好处,那就是可以通过注入具体实现的模拟版本,让对象更加容易进行单元测试

3.3.7. 使用组合而不是继承可能需要你多写点代码

3.3.7.1. 因为你可能需要用接口而不是具体的引用来定义依赖关系,但这也会使代码摆脱依赖关系

4. 不要使用类

4.1. 类会产生少量的引用间接开销(reference indirection overhead)

4.1.1. 与值类型(value type)相比,它们更侧重间接方面

4.2. 值类型可以是有价值的

4.2.1. C#中的原始类型,如int、long和double,就是值类型

4.2.2. 可以用enum和struct这样的结构来组成你自己的值类型

4.3. enum用来保存离散的序数值(discrete ordinal value)是很不错的

4.3.1. 每一个你定义的enum的类型都是不同的,这让值拥有了类型安全(type-safe)

4.3.2. enum也是值类型,也就是说其值和整数值的传递速度是一样快的

4.4. 类会产生一点存储开销。每个类在实例化的时候都需要保留一个对象头和虚拟方法表

4.4.1. 类是在堆上分配的,而且它们会被回收

4.5. 结构是轻量级的类

4.5.1. 它们被分配在栈上,因为它们是值类型

4.5.2. 将一个结构值分配给一个变量意味着复制其内容,因为没有一个引用代表它

4.5.3. 对于任何大于指针大小的数据,复制的速度比仅传递引用的速度要慢

4.5.4. 因为结构是值类型,将它赋值给另一个结构时,会同时创建该结构所有内容的副本,而不仅仅是创建一个副本内容的引用

4.6. 在.NET中,栈的大小为1MB,而堆却可以包含TB级的数据

4.7. 栈的存取速度快,但是如果用大型结构去填充它,它很容易就被填满

4.8. 调用栈可以非常快速和有效地存储东西

4.8.1. 由于它们不受垃圾回收的影响,因此它们非常适用于处理较小的值,而且开销也较少

4.8.2. 因为它们不是引用类型,所以它们也不能为null,这使得结构不可能出现空引用异常

4.9. 你不能共享对它们的通用引用,这意味着你不能从不同的引用中更改通用实例

4.10. 当类较大时,可以提供更有效的存储,因为在赋值时只有它们的引用会被复制

4.11. 请放心地将结构用于不需要继承的小型、不可变的值类型

5. 糟糕代码

5.1. 最佳实践来自糟糕的代码,然而糟糕的代码也可能来自最佳实践的胡乱应用

5.2. 不要使用If/Else

5.2.1. If/Else让我们以一种类似于流程图的方式来表达程序的逻辑,但它也会使代码的可读性降低

5.2.2. 避免不必要缩进的一般原则是尽可能早地退出函数,并且在流程中已经暗示else语义时要避免使用else

5.2.3. “愉快路径”(happy path)

5.2.3.1. 没有其他错误发生时执行的代码部分

5.2.4. 通过将else语句转换为return语句,我们可以让读者更容易地识别愉快路径,而不是形成if语句的“俄罗斯套娃”

5.2.5. 尽早验证,并尽早返回

5.3. 使用goto

5.3.1. goto语句可以将程序的执行直接转移到一个任意的目标点

5.3.2. 许多现代编程语言不再有相当于goto语句的东西

5.3.3. C#有一个场景中非常有效:消除函数中多余的退出点

5.3.3.1. 退出点(exit point)是指函数中导致其返回给调用者的语句
5.3.3.2. 在C#中,每个返回语句都是一个退出点
5.3.3.3. 可以使用goto语句来清理,但实际上它在消除冗余方面更有优势
5.3.3.4. 如果你想在通用退出代码(common exit code)中增加更多的内容,你只需要把它添加到一个地方
5.3.3.4.1. 通过使用goto语句,我们实际上保持了代码的可读性,减少了缩进,节省了自己的时间,并使将来的修改更加容易,因为我们只需要修改一次
5.3.3.5. C# 7.0引入了局部函数,可以用来执行同样的工作,也许是以一种更容易理解的方式
5.3.3.6. 使用局部函数还允许我们在函数的顶部声明错误处理语句

6. 不写代码注释

6.1. 如果代码足够容易理解,则不需要编写代码注释

6.2. 使用那些无关的注释可能会毁掉代码的可读性

6.3. 若非必要,不写注释

6.4. 选个好名字

6.4.1. 使用HTTP、JSON、ID或者DB等广为人知的缩写倒还是可以的,但千万不要缩短单词

6.4.2. 当你选择一个描述性的名字时,你不必写一个完整的句子来解释它的功能

6.5. 充分利用函数

6.5.1. 短小的函数更易于理解

6.5.2. 尽量让函数尺寸适合开发人员的屏幕

6.5.2.1. 阅读代码时,来回滚动屏幕会让你不适,你应该一眼就能看到函数的全貌

6.5.3. 绝对不要把多个语句放在一行,一个语句至少得占用一行

6.6. 避免不必要的注释会使有用的注释像珍珠一样闪闪发光。这是使注释有用的唯一方法

6.7. 写了注释就能让你的代码很容易懂,前提是你的代码本身就精巧、易于理解

6.8. 公共API

6.8.1. 用户可能无法接触到代码

7. 要点

7.1. 避免因为混淆逻辑上的依赖界限而写出那些刚性代码

7.2. 不要害怕从头开始做一项工作,因为当你下次做的时候,你会发现进展要快得多

7.3. 请勇于拆分代码,并修整它

7.4. 保持代码最新并定期解决它所引起的问题

7.5. 重复代码而不是复用代码,避免混淆各代码逻辑的作用

7.6. 把抽象当作一项投资

7.6.1. 将抽象模型构思得巧妙一些,这样你将来写代码就会花更少的时间

7.7. 不要让使用的外部库来限制了你的设计

7.8. 为了避免将代码束缚在特定的层次结构,更推荐组合而不是继承

7.9. 尽量让代码保持自顶向下的风格,以便于阅读

7.10. 提前退出函数并避免使用else

7.10.1. return

7.11. 使用goto语句

7.11.1. 使用一个本地函数来将公共代码保存在一个地方

7.12. 避免随意、多余的注释

7.12.1. 只写有用的注释

7.13. 利用好变量和函数本身命名,让你写的代码更具可读性

7.14. 将函数划分为易于理解的子函数,以尽可能保持代码的可读性

相关文章:

读程序员的制胜技笔记04_有用的反模式(下)

1. 重新发明轮子 1.1. 发明家的特质就是要用质疑的心态对待所有事物,你从未停下质疑,那你将不可避免地成为一个发明家 1.2. 并非所有的事情都有现成的轮子可以拿来用 1.3. 自己重新写一个新的API,最终调用你使用的库 1.3.1. 你的API应该是…...

linux驱动开发环境搭建

使用的是parallel 创建的ubuntu 16.04 ubuntu20.04虚拟机 源码准备 # 先查看本机版本 $ uname -r 5.15.0-86-generic# 搜索相关源码 $ sudo apt-cache search linux-source [sudo] password for showme: linux-source - Linux kernel source with Ubuntu patches linux-sourc…...

Qt利用VCPKG和CMake和OpenCV和Tesseract实现中英文OCR

文章目录 1. 开发平台2. 下载文件2.1 下载安装 OpenCV 库2.2 下载安装 Tesseract-OCR库2.3 下载训练好的语言包 3. CMakeLists.txt 内容4. Main.cpp4.1 中英文混合OCR 5. 在Qt Creator 中设置 CMake vcpkg5.1 在初始化配置文件里修改5.2 在构建配置里修改 说明:在Q…...

Day20力扣打卡

打卡记录 数组中两个数的最大异或值&#xff08;位运算&#xff09; 链接 二进制位上从高位向低位进行模拟&#xff0c;看数组中是否有满足此情况的数字。具体题解 class Solution { public:int findMaximumXOR(vector<int>& nums) {int mx *max_element(nums.be…...

设计模式之两阶段终止模式

文章目录 1. 简介 2. 常见思路3. 代码实战 1. 简介 两阶段终止模式&#xff08;Two-Phase Termination Pattern&#xff09;是一种软件设计模式&#xff0c;用于管理线程或进程的生命周期。它包括两个阶段&#xff1a;第一阶段是准备阶段&#xff0c;该阶段用于准备线程或进程…...

Dubbo捕获自定义异常

一.问题描述 Dubbo远程服务提供者抛出的自定义异常无法被消费方正常捕获&#xff0c;消费方捕获的自定义异常全部变成RuntimeException&#xff0c;使用起来很不方便。 二.原因分析 相关源码 /** Licensed to the Apache Software Foundation (ASF) under one or more* con…...

Leetcode刷题详解——求根节点到叶节点数字之和

1. 题目链接&#xff1a;129. 求根节点到叶节点数字之和 2. 题目描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a; 例如&#xff0c;从根节点到叶节点的路径 1…...

emq集群配置nginx做负载均衡

emq集群配置nginx做负载均衡 创建 EMQ X 节点集群 emqx 集群搭建 例如: 节点IP 地址emqx192.168.1.17192.168.1.17emqx192.168.1.18192.168.1.18emqx192.168.1.19192.168.1.19 配置 /etc/nginx/nginx.conf mqtt集群搭建并使用nginx做负载均衡_亲测得结论 示例: vim /et…...

【JAVA学习笔记】60 - 坦克大战1.0-绘图坐标体系、事件处理机制

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter16/src/com/yinhai 绘图坐标体系 一、基本介绍 下图说明了Java坐标系。坐标原点位于左上角&#xff0c;以像素为单位。在Java坐标系中&#xff0c;第一个是x坐标&#xff0c;表示当前位置为…...

Android13 安装谷歌GMS导致打开蓝牙失败解决方法

Android13 安装谷歌GMS导致打开蓝牙失败解决方法 文章目录 Android13 安装谷歌GMS导致打开蓝牙失败解决方法一、前言二、解决方法1、简单的解决方法2、添加属性和日志解决 三、分析1、查看异常日志2、 查看蓝牙相关日志 四、总结1、Android13 安装谷歌GMS导致打开蓝牙失败具体原…...

独创改进 | RT-DETR 引入双向级联特征融合结构 RepBi-PAN | 附手绘结构图原图

本专栏内容均为博主独家全网首发,未经授权,任何形式的复制、转载、洗稿或传播行为均属违法侵权行为,一经发现将采取法律手段维护合法权益。我们对所有未经授权传播行为保留追究责任的权利。请尊重原创,支持创作者的努力,共同维护网络知识产权。 文章目录 YOLOv6贡献RepBi-…...

Ubuntu下安装vscode,并解决终端打不开vscode的问题

Visual Studio Code安装 1&#xff0c;使用 apt 安装 Visual Studio Code 在官方的微软 Apt 源仓库中可用。按照下面的步骤进行即可&#xff1a; 以 sudo 用户身份运行下面的命令&#xff0c;更新软件包索引&#xff0c;并且安装依赖软件&#xff1a; sudo apt update sud…...

Spring Boot Actuator 漏洞利用

文章目录 前言敏感信息泄露env 泄露配置信息trace 泄露用户请求信息mappings 泄露路由信息heapdump泄露堆栈信息 前言 spring对应两个版本&#xff0c;分别是Spring Boot 2.x和Spring Boot 1.x&#xff0c;因此后面漏洞利用的payload也会有所不同 敏感信息泄露 env 泄露配置信…...

acwing算法基础之数据结构--trie算法

目录 1 基础知识2 模板3 工程化 1 基础知识 trie树算法&#xff0c;也叫作字典树算法。 用处&#xff1a;用来高效存储和查找字符串集合的数据结构。 &#xff08;一&#xff09; 定义变量。 const int N 1e5 10; int son[N][26], cnt[N], idx; char str[N];&#xff08;二…...

ES from+size>10000报错

参考博客 from size > 10000就会报错 Result window is too large, from size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_…...

(04)Mycat实现分库

1、如何选择分库表 #客户表 rows:20万 CREATE TABLE customer(id INT AUTO_INCREMENT,NAME VARCHAR(200),PRIMARY KEY(id) );#订单表 rows:600万 CREATE TABLE orders(id INT AUTO_INCREMENT,order_type INT,customer_id INT,amount DECIMAL(10,2),PRIMARY KEY(id) ); #…...

DeepSORT多目标跟踪——算法流程与源码解析

一、目标检测与目标追踪 1. 目标检测 在目标检测任务中&#xff0c;主要目标是识别图像或视频帧中存在的物体的位置和类别信息。这意味着目标检测算法需要定位物体的边界框&#xff08;Bounding Box&#xff09;并确定每个边界框内的物体属于哪个类别&#xff08;如人、汽车、…...

C++查漏补缺与新标准(C++20,C++17,C++11)02 C++快速回顾(二)

本内容参考C20高级编程 C风格的数组 //形如 int myArray[3]{2};一个比较新颖的获取C风格数组大小的函数std::size()&#xff0c;返回size_t类型&#xff08;在中定义的无符号整数&#xff09; #include <iostream> using namespace std;int main() {int myArray[5] {…...

红米K40功能介绍

红米K40是小米旗下的一款高性能智能手机。以下是红米K40的一些功能介绍及新增功能&#xff1a; 1.高性能处理器&#xff1a;红米K40搭载了骁龙870处理器&#xff0c;提供强大的性能和流畅的操作体验。 2.120Hz刷新率屏幕&#xff1a;红米K40采用了6.67英寸的AMOLED全面屏&…...

壹[1],Opencv常用结构

1&#xff0c;Point类&#xff1a;点表示 point表示二维结构的点,(x,y) cv::Point point; point.x 100; point.y 100; 2&#xff0c;Scalar类&#xff1a;颜色表示 cv::Scalar colorBlue(255,0,0);//蓝色 cv::Scalar colorGreen(0, 255, 0);//绿色 cv::Scalar colorRed(0, …...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

STM32标准库-ADC数模转换器

文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”&#xff1a;输入模块&#xff08;GPIO、温度、V_REFINT&#xff09;1.4.2 信号 “调度站”&#xff1a;多路开关1.4.3 信号 “加工厂”&#xff1a;ADC 转换器&#xff08;规则组 注入…...