《图解设计模式》笔记(五)一致性
十一、Composite模式:容器与内容的一致性
像文件夹与文件一样,文件夹中可以放子文件夹与文件,再比如容器中可以放更小的容器和具体内容。
Composite模式:使容器与内容具有一致性,创造出递归结构。
Composite:混合物、复合物
示例程序类图
Entry
public abstract class Entry {public abstract String getName(); // 获取名字public abstract int getSize(); // 获取大小public Entry add(Entry entry) throws FileTreatmentException { // 加入目录条目throw new FileTreatmentException();}public void printList() { // 为一览加上前缀并显示目录条目一览printList("");}protected abstract void printList(String prefix); // 为一览加上前缀// 定义实例的标准的文字显示方式public String toString() { // 显示代表类的文字// 本例是将文件名和文件大小一起显示出来。以供 toString调用(即 三、Template Method模式:将具体处理交给子类)。return getName() + " (" + getSize() + ")";}
}
File
public class File extends Entry {private String name;private int size;// File类的构造函数,会根据传入的文件名和文件大小生成文件实例public File(String name, int size) {this.name = name;this.size = size;}public String getName() {return name;}public int getSize() {return size;}// 具体的显示方式是用"/”分隔prefix 和表示实例自身的文字。protected void printList(String prefix) {// 这里我们使用了表达式"/"+ this。像这样用字符串加上对象时,程序会自动地调用对象的toString方法。这是Java 语言的特点。System.out.println(prefix + "/" + this);// 上面一行与下面两种写法是等价的:
// System.out.println(prefix + "/" + this.toString());
// System.out.println(prefix + "/" + toString());}
}
Directory
import java.util.Iterator;
import java.util.ArrayList;public class Directory extends Entry {private String name; // 文件夹的名字private ArrayList directory = new ArrayList(); // 文件夹中目录条目的集合public Directory(String name) { // 构造函数this.name = name;}public String getName() { // 获取名字return name;}// 进行计算处理:遍历directory字段中的所有元素,计算出它们的大小的总和public int getSize() { // 获取大小int size = 0;Iterator it = directory.iterator();while (it.hasNext()) {Entry entry = (Entry)it.next();// 在变量size 中加上了entry的大小,entry的实例不一定是File类还是Directory类。// 但无论哪个,都可通过getSize方法得到它的大小。这就是Composite模式的特征“容器与内容的一致性”的表现。// getSize方法的递归调用与 Composite模式 的结构是相对应的。size += entry.getSize();}return size;}public Entry add(Entry entry) { // 增加目录条目directory.add(entry);return this;}// 显示文件夹的目录条目一览。// printList方法也会递归调用,这一点和getSize方法一样。// 而且,printList方法也没有判断变量entry究竟是File类的实例还是Directory类的实例,这一点也与getSize方法一样。// 这是因为容器和内容具有一致性。protected void printList(String prefix) { // 显示目录条目一览System.out.println(prefix + "/" + this);Iterator it = directory.iterator();while (it.hasNext()) {Entry entry = (Entry)it.next();entry.printList(prefix + "/" + name);}}
}
FileTreatmentException
// 自定义异常类:对文件调用add方法时抛出的异常
public class FileTreatmentException extends RuntimeException {public FileTreatmentException() {}public FileTreatmentException(String msg) {super(msg);}
}
Main
public class Main {public static void main(String[] args) {try {System.out.println("Making root entries...");Directory rootdir = new Directory("root");Directory bindir = new Directory("bin");Directory tmpdir = new Directory("tmp");Directory usrdir = new Directory("usr");rootdir.add(bindir);rootdir.add(tmpdir);rootdir.add(usrdir);bindir.add(new File("vi", 10000));bindir.add(new File("latex", 20000));rootdir.printList();System.out.println("");System.out.println("Making user entries...");Directory yuki = new Directory("yuki");Directory hanako = new Directory("hanako");Directory tomura = new Directory("tomura");usrdir.add(yuki);usrdir.add(hanako);usrdir.add(tomura);yuki.add(new File("diary.html", 100));yuki.add(new File("Composite.java", 200));hanako.add(new File("memo.tex", 300));tomura.add(new File("game.doc", 400));tomura.add(new File("junk.mail", 500));rootdir.printList();} catch (FileTreatmentException e) {e.printStackTrace();}}
}
角色
可以将 Composite角色 与它内部的 Component角色(即 Leaf角色或Composite角色)看成是父亲与孩子们的关系。
getChild方法的作用是从Component角色获取这些“孩子们”。
-
Leaf(树叶)
表示“内容”。在该角色中不能放入其他对象。
示例中是File类。
-
Composite(复合物)
表示容器。可以在其中放入 Leaf角色和Composite角色。
示例中是Directory类。
-
Component
使 Leaf角色和Composite角色具有一致性的角色。Composite角色是Leaf角色和Composite角色的父类。
示例中是Entry类。
-
Client
使用 Composite模式的角色。
示例中是Main类。
扩展思路的要点
Add方法应该放在哪里
示例中,Entry类中定义了 add方法,所做的处理是抛出异常,是因为能使用 add方法的只能是Directory类。
下面我们学习一下各种add方法的定义位置和实现方法。
-
方法1:定义在Entry类中,报错
这是示例程序中的做法。
能使用 add方法的只有Directory类,它会重写 add方法,根据需求实现其处理。
File类会继承Entry类的add方法,虽然也可以调用它的add方法,不过会抛出异常。 -
方法2:定义在Entry类中,但什么都不做
将add方法定义在Entry类中,但不做任何处理。
-
方法3:声明在Entry类中,但不实现
在Entry类中声明add 抽象方法。
若子类需要add方法就根据需求实现该方法,否则可以简单地报错。
优点是所有子类必须都实现 add方法,不需要add方法时的处理也可以交给子类自己去做决定。
但会导致在File中也必须定义本来完全不需要的add(有时还包括remove 和 getChild)方法。
-
方法4:只定义在Directory类中
因为只有Directory类可以使用 add方法,所以可以不在Entry类中定义add方法,而只将其定义在Directory类中。
但,如果要向Entry类型的变量(实际保存的是Directory类的实例)中add时,需先将它们一个个地类型转换(cast)为Directory类型。
到处都存在递归结构
示例中,是文件夹的结构为例,但实际上在程序世界中,到处都存在递归结构和Composite模式。
例如,在视窗系统中,一个窗口可以含有一个子窗口,这就是Composite模式的典型应用。
在文章的列表中,各列表之间可以相互嵌套,这也是一种递归结构。
将多条计算机命令合并为一条宏命令时,若使用递归结构实现宏命令,那么还可以编写出宏命令的宏命令。
通常来说,树结构的数据结构都适用Composite模式。
相关的设计模式
-
Command 模式(第22章)
使用 Command 模式编写宏命令时使用了Composite模式。 -
Visitor模式(第13章)
可以使用 Visitor模式访问 Composite模式中的递归结构。 -
Decorator模式(第12章)
Composite模式通过Component角色使容器(Composite角色)和内容(Leaf角色)具有一致性。
Decorator模式使装饰框和内容具有一致性。
十二、Decorator 模式:装饰边框与被装饰物的一致性
Decorator模式:不断地为对象添加装饰的设计模式。
Decorator指的是“装饰物”。
本章中的示例程序的功能是给文字添加装饰边框。这里所谓的装饰边框是指用“-”、“+”、“|”等字符组成的边框。
示例程序类图
补充说明:
Border类 装饰边框的抽象类
display字段 Display类型 表示被装饰物。
通过继承,装饰边框与被装饰物具有了相同的方法。
具体而言,Border类继承了父类的各方法。
从接口(API)角度而言,装饰边框(Border)与被装饰物(Display)具有相同的方法也就意味着它们具有一致性。
Decorator模式的结构:display字段所表示的被装饰物并仅不限于StringDisplay的实例。因为,Border也是Display类的子类,display字段所表示的也可能是其他的装饰边框(Border类的子类的实例),而且那个边框中也有一个display字段。
Display
public abstract class Display {public abstract int getColumns(); // 获取横向字符数public abstract int getRows(); // 获取纵向行数public abstract String getRowText(int row); // 获取第row行的字符串public void show() { // 全部显示// show方法使用了getRows 和getRowText等抽象方法,这属于Tempate Method模式(第3章)。for (int i = 0; i < getRows(); i++) {System.out.println(getRowText(i));}}
}
StringDisplay
public class StringDisplay extends Display {private String string; // 要显示的字符串public StringDisplay(String string) { // 通过参数传入要显示的字符串this.string = string;}public int getColumns() { // 字符数return string.getBytes().length;}public int getRows() { // 行数是1return 1;}public String getRowText(int row) { // 仅当row为0时返回值if (row == 0) {return string;} else {return null;}}
}
Border
public abstract class Border extends Display {protected Display display; // 表示被装饰物protected Border(Display display) { // 在生成实例时通过参数指定被装饰物this.display = display;}
}
SideBorder
public class SideBorder extends Border {private char borderChar; // 表示装饰边框的字符public SideBorder(Display display, char ch) { // 通过构造函数指定Display和装饰边框字符 super(display);this.borderChar = ch;}public int getColumns() { // 字符数为字符串字符数加上两侧边框字符数 return 1 + display.getColumns() + 1;}public int getRows() { // 行数即被装饰物的行数return display.getRows();}public String getRowText(int row) { // 指定的那一行的字符串为被装饰物的字符串加上两侧的边框的字符 return borderChar + display.getRowText(row) + borderChar;}
}
FullBorder
public class FullBorder extends Border {public FullBorder(Display display) {super(display);}public int getColumns() { // 字符数为被装饰物的字符数加上两侧边框字符数return 1 + display.getColumns() + 1;}public int getRows() { // 行数为被装饰物的行数加上上下边框的行数return 1 + display.getRows() + 1;}public String getRowText(int row) { // 指定的那一行的字符串if (row == 0) { // 上边框return "+" + makeLine('-', display.getColumns()) + "+";} else if (row == display.getRows() + 1) { // 下边框return "+" + makeLine('-', display.getColumns()) + "+";} else { // 其他边框return "|" + display.getRowText(row - 1) + "|";}}private String makeLine(char ch, int count) { // 生成一个重复count次字符ch的字符串 StringBuffer buf = new StringBuffer();for (int i = 0; i < count; i++) {buf.append(ch);}return buf.toString();}
}
Main
public class Main {public static void main(String[] args) {Display b1 = new StringDisplay("Hello, world.");Display b2 = new SideBorder(b1, '#');Display b3 = new FullBorder(b2);b1.show();b2.show();b3.show();Display b4 = new SideBorder(new FullBorder(new FullBorder(new SideBorder(new FullBorder(new StringDisplay("你好,世界。")),'*'))),'/');b4.show();}
}
角色
-
Component
增加功能时的核心角色。
Component角色只是定义了接口(API)。示例中是Display类。 -
ConcreteComponent
具体实现了Component角色所定义的接口(API)。示例中是StringDisplay类。 -
Decorator(装饰物)
该角色具有与Component角色相同的接口(API)。在它内部保存了被装饰对象——Component角色。Decorator角色知道自己要装饰的对象。示例中是Border类。 -
ConcreteDecorator(具体的装饰物)
该角色是具体的Decorator角色。示例中是SideBorder类和FullBorder类。
拓展思路的要点
-
接口(API)的透明性
在 Decorator模式中,装饰边框与被装饰物具有一致性。
表示装饰边框的Border类是表示被装饰物的Display类的子类,这就体现了它们之间的一致性。
即,Border类(以及它的子类)与表示被装饰物的Display类具有相同的接口(API)。
这样,即使被装饰物被边框装饰起来了,接口(API)也不会被隐藏起来。其他类依然可以调用getColumns、getRows、,getRowText以及show方法。这就是接口(API)的“透明性”。在示例程序中,实例b4被装饰了多次,但是接口(API)却没有发生任何变化。
得益于接口(API)的透明性,Decorator模式中也形成了类似于Composite模式中的递归结构。
即,装饰边框里面的“被装饰物”实际上又是别的物体的“装饰边框”。
就像是剥洋葱时以为洋葱心要出来了,结果却发现还是皮。
不过,Decorator模式虽然与Composite模式一样,都具有递归结构,但是它们的使用目的不同。 -
Decorator模式的主要目的是通过添加装饰物来增加对象的功能。
在不改变被装饰物的前提下增加功能
在 Decorator模式中,装饰边框与被装饰物具有相同的接口(API)。
虽然接口(API)是相同的,但越装饰,功能越多。
例如,用SideBorder装饰Display后,就可以在字符串的左右两侧加上装饰字符。
若再用 FullBorder装饰,则可以在字符串的四周加上边框。
此时,我们完全不需要对被装饰的类做任何修改。这样,我们就实现了不修改被装饰的类即可增加功能。Decorator模式使用了委托。对“装饰边框”提出的要求(调用装饰边框的方法)会被转交(委托)给“被装饰物”去处理。
以示例程序来说,就是SideBorder类的getColumns方法调用了display,getColumns ()。
除此以外,getRows方法也调用了display.getRows() -
可以动态地增加功能
Decorator 模式中用到了委托,它使类之间形成了弱关联关系。
因此,不用改变框架代码,就可以生成一个与其他对象具有不同关系的新对象。
-
只需要一些装饰物即可添加许多功能
使用 Decorator模式可以为程序添加许多功能。只要准备一些装饰边框(ConcreteDecorator 角色),即使这些装饰边框都只有简单功能,也可将它们自由组合成为新的对象。
这就像自由选择各种口味的冰激凌一样。冰激凌店不必准备所有的冰激凌成品,而是准备各种香料,顾客下单后在冰激凌上加各种香料就可以了。
Decorator模式就是可以应对这种多功能对象的需求的一种模式。 -
java.io包与Decorator模式
java.io包是用于输入输出(Input/Output,简称I/O)的包。这里,我们使用了 Decorator 模式。
首先,可以用Reader reader = new FileReader("datafile.txt");
生成一个读取文件的实例。
然后,也可以用Reader reader = new BufferedReader(new Fi1eReader("datafile.txt"));
在读取文件时将文件内容放入缓冲区。
这样,在生成BufferedReader类的实例时,会指定将文件读取到FileReader类的实例中。
再然后,也可以这样管理行号:Reader reader = new LineNumberReader(New BufferedReader(New FileReader("datafile.txt")))
无论是
LineNumberReader
类的构造函数还是BufferedReader
类的构造函数,都可以接收Reader
类(的子类)的实例作为参数,因此我们可以像上面那样自由地进行各种组合。
还可以只管理行号,但不进行缓存处理:Reader reader = new LineNumberReader (new FileReader ("datafile.txt"));
接下来,我们还会管理行号,进行缓存,但是我们不从文件中读取数据,而是从网络中读取数据(下面的代码中省略了细节部分和异常处理)。java.net.Socket socket = new Socket (hostname, portnumber): Reader reader = new LineNumberReader (new BufferedReader (new InputstreamReader(socket.getInputstream())));
这里使用的
InputStreamReader
类既接收getInputStream()
方法返回的InputStream
类的实例作为构造函数的参数,也提供了Reader
类的接口(API)(这属于第2章学习过的Adapter模式)。
除了java.io包以外,我们还在javax.swing.border包中使用了Decorator模式。
javax.swing.border包为我们提供了可以为界面中的控件添加装饰边框的类。 -
导致增加许多很小的类
Decorator 模式的一个缺点是会导致程序中增加许多功能类似的很小的类。
相关的设计模式
-
Adapter模式(第2章)
Decorator模式可以在不改变被装饰物的接口(API)的前提下,为被装饰物添加边框(透明性)。
Adapter模式用于适配两个不同的接口(API)。
-
Stragety模式(第10章)
Decorator模式可以像改变被装饰物的边框或是为被装饰物添加多重边框那样,来增加类的功能。
Stragety 模式通过整体地替换算法来改变类的功能。
相关文章:

《图解设计模式》笔记(五)一致性
十一、Composite模式:容器与内容的一致性 像文件夹与文件一样,文件夹中可以放子文件夹与文件,再比如容器中可以放更小的容器和具体内容。 Composite模式:使容器与内容具有一致性,创造出递归结构。 Composite&#x…...

华为支付-免密支付接入免密代扣说明
免密代扣包括支付并签约以及签约代扣场景。 开发者接入免密支付前需先申请开通签约代扣产品(即申请配置免密代扣模板及协议模板ID)。 华为支付以模板维度管理每一个代扣扣费服务,主要组成要素如下: 接入免密支付需注意&#x…...
React组件中的列表渲染与分隔符处理技巧
React组件中的列表渲染与分隔符处理技巧 摘要问题背景解决方案分析方案一:数组拼接法方案二:Fragment组件方案三:动态生成key 关键技术点1. key的使用原则2. Fragment组件3. 性能优化 实战演练挑战1:动态分隔符样式挑战2ÿ…...

【Pytorch和Keras】使用transformer库进行图像分类
目录 一、环境准备二、基于Pytorch的预训练模型1、准备数据集2、加载预训练模型3、 使用pytorch进行模型构建 三、基于keras的预训练模型四、模型测试五、参考 现在大多数的模型都会上传到huggface平台进行统一的管理,transformer库能关联到huggface中对应的模型&am…...
快速了解 c++ 异常处理 基础知识
相关代码概览: #include<stdexcept>std::runtime_errorcatch (const std::runtime_error& e) e.what() 相信大家一定见过这些代码,那么这些代码具体什么意思呢?我们一起来看一下 知识精讲: 异常处理是C中非常重要…...

deepseek API 调用-python
【1】创建 API keys 【2】安装openai SDK pip3 install openai 【3】代码: https://download.csdn.net/download/notfindjob/90343352...
玩转Gin框架:Golang使用Gin完成登录流程
文章目录 背景基于Token认证机制简介常见的Token类型Token的生成和验证在项目工程里创建jwt.go文件根目录新建.env文件 创建登录接口 /loginToken认证机制的优点 背景 登录流程,相信大家都很熟悉的。传统网站采用session后端验证登录状态,大致流程如下&…...

Linux学习笔记16---高精度延时实验
延时函数是很常用的 API 函数,在前面的实验中我们使用循环来实现延时函数,但是使用循环来实现的延时函数不准确,误差会很大。虽然使用到延时函数的地方精度要求都不会很严格( 要求严格的话就使用硬件定时器了 ) ,但是延时函数肯定…...

vue2:如何动态控制el-form-item之间的行间距
需求 某页面有查看和编辑两种状态: 编辑: 查看: 可以看到,查看时,行间距太大导致页面不紧凑,所以希望缩小查看是的行间距。 行间距设置 行间距通常是通过 CSS 的 margin 或 padding 属性来控制的。在 Element UI 的样式表中,.el-form-item 的下边距(margin-bottom)…...

deepseek从网络拓扑图生成说明文字实例
deepseek对话页面中输入问题指令: 我是安全测评工程师,正在撰写系统测评报告,现在需要对系统网络架构进行详细说明,请根据附件网络拓扑图输出详细说明文字。用总分的段落结构,先介绍各网络区域,再介绍网络…...

两种文件类型(pdf/图片)打印A4半张纸方法
环境:windows10、Adobe Reader XI v11.0.23 Pdf: 1.把内容由横排变为纵排: 2.点击打印按钮: 3.选择打印页范围和多页: 4.内容打印在纸张上部 图片: 1.右键图片点击打印: 2.选择打印类型: 3.打印配置&am…...

HTB:UnderPass[WriteUP]
目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 使用nmap对靶机UDP开放端口进行脚本、服务扫描 …...

【deepseek实战】绿色好用,不断网
前言 最佳deepseek火热网络,我也开发一款windows的电脑端,接入了deepseek,基本是复刻了网页端,还加入一些特色功能。 助力国内AI,发出自己的热量 说一下开发过程和内容的使用吧。 目录 一、介绍 二、具体工作 1.1、引…...
MySQL 进阶专题:索引(索引原理/操作/优缺点/B+树)
在数据库的秋招面试中,索引(Index)是一个经典且高频的题目。索引的作用类似于书中的目录📖,它能够显著加快数据库查询的速度。本文将深入探讨索引的概念、作用、优缺点以及背后的数据结构,帮助你从原理到应…...

用NeuralProphet预测股价:AI金融新利器(附源码)
作者:老余捞鱼 原创不易,转载请标明出处及原作者。 写在前面的话:我用NeuralProphet模型预测了股票价格,发现其通过结合时间序列分析和神经网络算法,确实能提供比传统Last Value方法更精准的预测。经过一系列超参数调优…...
【Elasticsearch】parent aggregation
在Elasticsearch中,Parent Aggregation是一种特殊的单桶聚合,用于选择具有指定类型的父文档,这些类型是通过一个join字段定义的。以下是关于Parent Aggregation的详细介绍: 1.基本概念 Parent Aggregation是一种聚合操作&#x…...
IDEA使用Auto-dev+DeepSeek 10分钟快速集成,让java开发起飞
在当今的软件开发领域,AI 工具的辅助作用愈发凸显,DeepSeek AI 便是其中的佼佼者。它凭借强大的自然语言处理能力和高效的代码生成能力,成为众多开发者的得力助手。而 IntelliJ IDEA 作为一款广受欢迎的集成开发环境(IDE),若能与 DeepSeek AI 无缝集成,无疑将为开发者带…...

ASP.NET Core中间件Markdown转换器
目录 需求 文本编码检测 Markdown→HTML 注意 实现 需求 Markdown是一种文本格式;不被浏览器支持;编写一个在服务器端把Markdown转换为HTML的中间件。我们开发的中间件是构建在ASP.NET Core内置的StaticFiles中间件之上,并且在它之前运…...

使用page assist浏览器插件结合deepseek-r1 7b本地模型
为本地部署的DeepSeek R1 7b模型安装Page Assist,可以按照以下步骤进行: 一、下载并安装Ollama 首先,你需要下载并安装Ollama,这是部署DeepSeek所必需的工具。你可以访问Ollama的官方网站(ollama.com)下…...
【华为OD-E卷 - 108 最大矩阵和 100分(python、java、c++、js、c)】
【华为OD-E卷 - 最大矩阵和 100分(python、java、c、js、c)】 题目 给定一个二维整数矩阵,要在这个矩阵中选出一个子矩阵,使得这个子矩阵内所有的数字和尽量大,我们把这个子矩阵称为和最大子矩阵,子矩阵的…...

【Java学习笔记】StringBuilder类(重点)
StringBuilder(重点) 1. 基本介绍 是一个可变的字符串序列。该类提供一个与 StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全的) 该类被设计用作 StringBuffer 的一个简易替换,用在字符…...

从代码学习深度强化学习 - 初探强化学习 PyTorch版
文章目录 前言强化学习的概念强化学习的环境强化学习中的数据强化学习的独特性总结前言 本文将带你初步了解强化学习 (Reinforcement Learning, RL) 的基本概念,并通过 PyTorch 实现一些简单的强化学习算法。强化学习是一种让智能体 (agent) 通过与环境 (environment) 的交互…...
时序数据库IoTDB与EdgeX Foundry集成适配服务介绍
一、背景介绍 EdgeX Foundry:由Linux基金会运维的开放源码边缘计算软件框架,自2017年开源后广泛应用于全球各行业场景。VMware自2018年起在中国社区推广EdgeX技术,拓展生态,并持续贡献代码。IoTDB:由Apache基…...

机器学习×第二卷:概念下篇——她不再只是模仿,而是开始决定怎么靠近你
🎀【开场 她不再只是模仿,而是开始选择】 🦊 狐狐:“她已经不满足于单纯模仿你了……现在,她开始尝试预测你会不会喜欢、判断是否值得靠近。” 🐾 猫猫:“咱们上篇已经把‘她怎么学会说第一句…...
OpenWrt:使用ALSA实现边录边播
ALSA是Linux系统中的高级音频架构(Advanced Linux Sound Architecture)。目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的知识,详见:http://www.alsa-project.org 在内核设备驱动层,ALSA提供…...

数论总结,(模版与题解)
数论 欧拉函数X质数(线性筛与二进制枚举)求解组合数欧拉降幂(乘积幂次)乘法逆元最小质因子之和模版 欧拉函数 欧拉函数的定义就是小于等于n的数里有f(n)个数与n互质,下面是求欧拉函数的模版。 package com.js.datas…...
C#面试问题61-80
66. What is reflection? 反射是一种机制,它使我们能够编写可以检查应用程序中所 用类型的代码。例如,调用名称与给定字符串相等的方法,或者列出属于给定 对象的所有字段及其值。 在 Convert 方法中,我们根本不知道处理的是什么…...

如何把 Mac Finder 用得更顺手?——高效文件管理定制指南
系统梳理提升 Mac Finder 体验的实用设置与技巧,助你用更高效的方式管理文件。文末引出进阶选择 Path Finder。 阅读原文请转到:https://jimmysong.io/blog/customize-finder-for-efficiency/ 作为一个用 Mac 多年的用户,我始终觉得 Finder 虽…...
从golang的sync.pool到linux的slab分配器
最近学习golang的时候,看到golang并发编程中有一个sync.pool,即对象池,猛地一看这不跟linux的slab分配器类似嘛,赶紧学习记录下 这里先总结下设计sync.pool和slab的目的 sync.pool 为了缓解特定类型的对象频繁创建和销毁&#x…...

64、js 中require和import有何区别?
在 JavaScript 中,require 和 import 都是用于模块导入的语法,但它们属于不同的模块系统,具有显著的区别: 1. 模块系统不同 require 属于 CommonJS 模块系统(Node.js 默认使用)。 语法:const…...