小程序中的大道理--综述
前言
以下将用一个小程序来探讨一些大道理, 这些大道理包括可扩展性, 抽象与封装, 可维护性, 健壮性, 团队合作, 工具的利用, 可测试性, 自顶向下, 分而治之, 分层, 可读性, 模块化, 松耦合, MVC, 领域模型, 甚至对称性, 香农的信息论等等.
为什么不用大程序来说大道理呢?
因为大程序太大了, 代码一端上来, 读者就晕菜了, 看不过来甚至压根不想去看, 这样说理就很抽象了, 效果反而不好.
小程序中也能说出大道理来吗?
我们有句话, 叫"以小见大", 我们又常常有种说法, 叫:
麻雀虽小, 五毒俱全. (咦? 好像应该是五脏俱全…总之你明白我的意思就好了. )
所以呢, 小程序也是可以来说大道理的, 而且小程序又有短小的特点, 大家看得也没那么累, 也很快能看懂. 毕竟那种代码, 叫什么来着, “意大利面条式的代码”, 大家在实际的开发中, 已经见得太多了.
意大利面(spaghetti), 翻译过来好像叫"通心粉", 非常长的一条条, 彼此缠缠绕绕的, 所以"意大利面条式的代码"就是又长又绕, 让人非常头痛的那种代码.
按我们的习惯, 也许叫它"裹脚布式的代码"大家觉得更熟悉, 更形象一点, 也正好符合"又长又臭"的特点.
啊, 说"又长又臭"可能有点刻薄了, 毕竟大家都可能写过这样的代码(本人就写过好多), 即便现在不会再写这样的代码, 想当年应该也是写过的, 除非你从一开始觉悟通天, 那我就无话可说了.
这种代码我们在工作中见得太多了, 所以这里就不再弄出来考验大家的毅力了, 闲话少提, 让我们看个简单的例子.
我们的例子
就是要打印出如下的一个三角形图案:
****
*****
当然了, 这只是以三行为一个示例, 我们的程序应该接受任意的正整数, 比如, 给一个 5, 就要能打出 5 行的类似的三角形来. 让我们来看看如何写出这样一个程序, 并在这个过程中借此兜售我们的大道理.
玩具式的代码
我知道很多"数学帝"可能一眼就被图案中的规律吸引过去了, 他们很快就指出星号是等差数列, 然后很快就弄出了计算每行前面要缩进多少个空格的公式, 然后呢, 一层循环, 二层循环, blablablah…然后最里面几条优雅而性感的 print 语句, 搞掂!一种智商上的优越感油然而生, 接着他们可能就要问:
这么简单的东西, 你也好意思拿出来讲?
下面是这样的一个代码, 能够完全实现以上要求(只演示了 3 行的情况):
public static void main(String[] args) {int i = 3;for (int j = 0; j < i; j++) {for (int k = 0; k < i - j; k++) {System.out.print(" ");}for (int z = 0; z < 2*j+1; z++) {System.out.print("*");}System.out.println();}
}
这里用的是 java 语言来演示, 包括以下的. 我相信像 java 这样烂大街的语言, 即使你没这个背景看懂也不是难事, 在代码中也不会用到什么高深的特性. (这一点皆因我的能力有限所导致, 而不是想装逼的意愿所能决定的~)
怎么说呢, 我们不要以上那种"玩具式"的代码(toy program), 我们要的是生产级(production, 生产环境)的代码.
生产级的代码
让我们来看看如何写出这样的代码.
可扩展性(Extensibility)
首先呢, print 语句是绝对要避免的. 你要明白, print 语句写得太死, 而需求是不断在变化的, 有句话是怎么说的?
唯一不变的就是变化本身.
客户今天跟你说的是要 print 这个图案, 你要是按着客户怎么说, 你就怎么做, 你可就惨了.
客户哪一天突然又会说, 再加点特性, 要能输出到文件;哪一天又说, 再加点 web service, 能供其它程序调用.
让我们多留点心眼, 代码如下:
public void printPattern(int lineCount) {String pattern = getPattern(lineCount);System.out.print(pattern);
}
我们先借助 getPattern
方法拿到要打印的内容, 这样, 如果哪天要输出到文件, 哪天要供 web service 调用, 我们都可以把这个 getPattern 方法提供出去.
我们只要多抽象出那么一层来, 就会给我们带来很多方便.
抽象与封装(Abstraction & Encapsulation)
抽象与封装同时也是很多其它特性的基础, 在后面我们还会不断说到这一主题.
getPattern
就是一个抽象, 是对一系列动作的一个封装.
可能有人会比较教条地认为抽象与封装只能在类层次中进行, 这常常导致在类的内部缺少必要的抽象层次, 常常是一大件事情在一个方法里完成, 方法巨大巨长无比, 这样的所谓面向对象编程不过是虚有其表, 其模块性甚至还比不上那些用面向过程语言写就的代码.
在 printPattern
层面, 我们不需要知道 getPattern 的细节, 我们只需要传入所需参数及定义好需要的返回值即可.
大道理: 定义好输入与输出, 描述清楚想要做的事, 先不用去管细节.
然后呢, 我们是不是需要手动去把这个方法写出来呢?
利用好你的工具(Tools)
你不用手动去做这些, 以 eclipse 为例, 只要把光标定位到错误的地方(可以按Ctrl+“.”(点)快速定位), 然后按下"Ctrl+1", 然后选择"Create Method"即可:
工具将根据传入参数及返回值自动为我们生成方法, 结果如下:
只要输入与输出定义清楚了, 工具就能自动帮我们生成方法定义, 这里默认它是 private
的, 我们可以把它改成 public
.
这里说的是 Eclipse 这个 IDE, 其它的我相信也会有类似的功能. 如果你偏好轻量级的文本编辑器, 那我就不敢说也一定有这些功能了.
利用好任务标识(Task Tags)
我们可以看到, 生成的代码里有个 TODO, 显示出了特别的颜色, 这是个任务标识.
类似的标识还有 FIXME, XXX, 甚至你还可以自定义标识.
打开 eclipse 的菜单-- windows–preferences, 在过滤框中输入"task tag":
这些有什么用呢? 我们可以看下, 在编辑器的左右侧, 都有显著的标志提示有个任务标识存在;在 Markers 视图里, 有列举出这些标识:
在代码质量分析工具 sonar 中, 它也会追踪这些标识. 下图是我在 sonar.oschina.net 上的一个项目的截图:
这些有什么用呢? 我们在写代码中, 写到一半, 很可能被某些难题卡住了, 为了不中断正常的流程, 我们先用个 TODO 来标识, 然后就可以继续地把一些简单的问题先处理完, 再回过头来对付这些.
又或者像现在这样, 我们生成了出来了这个方法, 工具为我们自动加了入"TODO"的标识, 毕竟方法的主体还没有, 可不巧的是, 现在到了下班时间了, 然后呢, 我们就可以存盘并提交到 svn 或者 git 上去了. 有人可能要说: "啊? 不是吧, 你的代码都没写完你怎么就提交了? "
没关系, 我们已经标识好了 TODO, 所以它会提醒我们还有工作是没做完的. 另外我们为何如此着急提交呢? 因为我们并不是在单打独斗:
团队合作(Teamwork)
我们前面说了, 我们可能还要做输出图案到文件的需求, 很可能你有个同事哥们, 他就正做着这个模块, 而他现在呢, 就在等着你这个 getPattern
方法. 你提交了, 他就可以继续写他的代码了:
package org.jcc.core.demo;public class PatternFile {private Pattern pattern;public PatternFile(Pattern pattern) {this.pattern = pattern;}public void generatePatternFile(int lineCount) {String content = pattern.getPattern(lineCount);saveInFile(content);}private void saveInFile(String content) {// TODO Auto-generated method stub//System.out.println(content);}
}
可以看到, 他的类依赖你的类, 在他的方法 generatePatternFile
里还调用了 getPattern
方法, 你没实现, 那又怎样呢? 接口好了就行了!
面向接口编程(Interface)
有人可能比较死板, 比较教条主义, 以为呢, 说到接口就一定要弄个 interface, 其实呢, 我们这个方法 getPattern
就是一个承诺, 一个约定, 一个协议, 也是一个广义上的接口.
有人可能要问, 你方法细节还没有实现, 他怎么测试? 别担心, 办法会有的:
利用 Mockito 来测试
代码如下:
@Test
public void testGeneratePatternFile() {// 用mockito来模拟接口的行为, 为此我们手动构建一个三行的图案Pattern pattern = Mockito.mock(Pattern.class);String mockContent = " *" + System.lineSeparator() + " ***" + System.lineSeparator() + "*****" + System.lineSeparator();// 当调用getPattern方法时, 就返回这里定义好的内容. Mockito.when(pattern.getPattern(3)).thenReturn(mockContent);// 测试generatePatternFile方法, 在它的里面将会调用getPattern方法PatternFile pf = new PatternFile(pattern);pf.generatePatternFile(3);// TODO 断言文件存在并且文件中的内容与mockContent一致// assert that file is exists and content in file is equals the mock content
}
以上我们用一个 mock
对象以及 when
, thenReturn
来主动模拟一个尚未实现的方法.
你也许对 Mock 之类的技术还不太了解, 但这些词表达的意思我想大家都不难明白. Mock 的更详细介绍请自行百度之.
借助 Mockito, 这个哥们就可以这样写好他的代码, 并完成他的测试了, 然后可以提交他的代码, 宣布工作完成, 接着他就可以飞到马尔代夫去度假去了.
可以看到, 尽管我们的功能八字还没一撇, 可只要我们坚持面向接口编程, 时时想着团队合作, 经常提交已经写好的代码, 特别是公共接口方面的代码, 我们的同事就能及时推进他们的工作, 甚至比我们还早完成, 这都是有可能的, 都是正常的, 也是我们应该追求的.
而利用好抽象及封装, 我们还能得到好几个好处:
可测试性(Testability)
通过以上举例, 可以看到, 我们可以手动构建一个图案, 并交给程序去判断(注: 为了简短起见, 代码中省略了具体的 assert
细节). 而如果是开头那样直接就打印了呢? 你根本没法让程序去判断, 只能通过人眼去观察输出, 这样就给 自动化的测试(Automatic Test) 带来了困难.
可重用性(Reusability)
当 getPattern
被抽象出来之后, 可以看到, 不但可以在 printPattern
方法里使用, 也可以在 generatePatternFile
方法里使用. 而如果按开头那样呢? 你没法复用, 你还是不得不重构;又或者你可能只是简单地把代码复制一遍了, 再作些改动.
当然, 现在这个程序很小, 全部拷贝一遍好像也很快, 但如果是很大的程序呢? 又或者我们又要拓展到可供 web service 调用, 难道就这样拷贝下去? 哪一天程序要做些小调整, 难道又要一一去修改吗?
不要重复(DRY: Don’t Repeat Yourself)
管理重复性一直都是程序开发中的重大关切, 在目前这个小程序里, 这一问题还不是那么迫切, 这个在此就不作详述, 以后会另写一些文章来做些介绍.
好了, 说了一大通, 绕了一大圈, 测试也测了, 同事也度假去了, 我们也要赶紧我们的工作. 那么接下来是不是赶紧写那些实现呢? 不!
我们已经介绍了不少的"ility"结尾的单词, 接下来还要说到!我有点担心大家说我"zhuangbility", 有句话说: “Don’t zhuangbility, zhuangbility leads to leipility”(莫装逼, 装逼遭雷劈), 没办法, 为了阐述这些大道理, 我也只好冒着被 leipility 的危险.
可维护性(Maintainability)
你首先把注释写好:
怎么说呢? 现在 IT 工作强度很大, 过劳死是不稀奇的事, 写着写着说不定哪天人就挂了. 一个人挂了不要紧, 工作可不能挂!(不是在说笑话, 貌似有些公司或老板表现出来的态度就是这样的~)
别人要能顺利接手你的活, 这是关键.
其实没必要说"挂了"这些不吉利的话, 也可能是有人要生了, 比如你老婆要生了, 你也休产假去了, 你写到一半, 老板把你的工作转交给你的同事.
试想, 要是一点注释都没有, 你的同事接手起来就很困难, 他要加班加点才能早点弄清你的代码的意图. 所以呢, 不要害了你的同事!把代码的可维护性做好, 大家的健康也才有更好的可维护性!
代码如下:
/*** 获取指定行数的图案, 比如3行时: * ** **** ****** * @param lineCount 指定的行数* @return 图案的字符串表示, 包括换行符在内*/
public String getPattern(int lineCount) {// TODO Auto-generated method stubreturn null;
}
其实, 良好的命名同样也是可维护性的关键, 比如上面的
getPattern
,lineCount
, 而不是像最前面那个示例中的 i, j, k, z 等乱七八糟的名字.另外, 丰富的抽象层次也是如此, 这点我们后续还会不断提及.
好了, 注释也写完了, 然后呢, 现在该轮到写那个该死的等差数列了吧? 不!
健壮性(Robustness)
Robustness 又常常音译成鲁棒性.
作者在大学时读的是自动化专业, 在那些自动控制理论里, 老出现什么鲁棒性, 看了让人犯晕, 不如直接叫健壮性.
我倒是想起了小时候老爸常买给我喝的 Robust(即乐百氏, 与娃哈哈类似的饮品), 味道是不错, 不过喝完身体挺没见得健壮到哪去, 也许喝得还不够多~
我们先要把判断做好, 输入负数或者输入的数字太大了, 你要拒绝它们, 同时在注释中也作出说明:
/*** 获取指定行数的图案, 比如3行时: * ** **** ****** * @param lineCount 指定的行数, 1-20之间* @return 图案的字符串表示, 包括换行符在内*/
public String getPattern(int lineCount) {if (lineCount < 1) {throw new IllegalArgumentException("行数不能小于1!");}if (lineCount > 20) {throw new IllegalArgumentException("行数不能大于20!");}// TODO return null;
}
我知道我在这里说这些, 有些人可能已经不耐烦了, 他们想着的是写那些有技巧的代码, 那些有挑战性的部分, 那些 tricky 的部分, 那些能体现出他们智商上的优越感的部分.
有个词是怎么说的, “rocket science”(火箭科学, 喻指那些高精尖的技术), 特别的有些刚毕业的心气很高的学生, 满脑子想的可能就是这些. 可是呢, 类似情况不是没有, 但通常是很少的:
骚年, 不是我在打击你, 你也许真的想多了. 工作上, 我们多数时候处理的都是一些细节的问题, 一些琐碎的事情, 一些按部就班的样板式的代码, 需要的不是多高的智商, 多么 tricky 的技巧, 要是是耐心, 细致, 严谨, 一丝不苟.
为何一开始就要把这些做好呢? 因为到了后面, 你就没时间去做了. 这一点你一定要相信我, 以下引自 wiki 的"90-90法则":
前 90% 的代码要花费你 90% 的开发时间, 剩余的 10% 的代码要花费你另一个 90% 的开发时间.
The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time.
–Tom Cargill, 贝尔实验室
而最后如果因为时间紧急, 就这样没保护就上了生产环境, 一旦出了问题, 你会花更多的时间去收拾这些烂摊子, 而最终你还是不得不将这些补上.
有一个"墨菲定律"(Murphy’s Law)大意是这么说的:
有可能出错的的东西一定会出错.
现在不擦屁股, 后面还有得擦. 你省掉了纸尿裤, 你的程序就裸奔了, 你就等着洗更多的外套.
我们也常说: "该来的一定会来. "如果用电影<<无间道>>里的话来说呢, 那就是:
“出来混, 迟早要还的”. (哇塞, 说得太精彩了. 这些编导或者剧作家不去写教科书太可惜了. )
所以呢, 不要有侥幸的心理, 把程序从一开始就写健壮才是正道.
小结
说了半天, 我们甚至连一行核心代码都没写, 不过, 文章至此倒是要先做一个阶段了结了. 我们说写代码有个原则, 那就是方法不能太长, 最好一个屏幕就能显示完, 否则看起来就很累了;自然的, 文章也不能写得太长, 否则写起来, 读起来都很累人.
所以呢, 虽然一开始那里提了好多的道理, 本来也是想一扒到底的, 但扒到一半发现已经很长了, 所以上半身扒完, 就此腰斩, 下半身留待后面继续扒, 下半身更精彩, 我们下回再见.
下一篇, 见 小程序中的大道理之二 .
相关文章:

小程序中的大道理--综述
前言 以下将用一个小程序来探讨一些大道理, 这些大道理包括可扩展性, 抽象与封装, 可维护性, 健壮性, 团队合作, 工具的利用, 可测试性, 自顶向下, 分而治之, 分层, 可读性, 模块化, 松耦合, MVC, 领域模型, 甚至对称性, 香农的信息论等等. 为什么不用大程序来说大道理呢? …...
tlais智能学习辅助系统-修改部门功能实现
学习黑马程序员的JavaWeb课程,自己写的部门信息修改部分程序 控制层: //DeptController.java /** * 根据ID查询部门信息 * param id * return */ GetMapping("/{id}") public Result select(PathVariable Integer id){log.info("查询id…...

GLM: 自回归空白填充的多任务预训练语言模型
当前,ChatGLM-6B 在自然语言处理领域日益流行。其卓越的技术特点和强大的语言建模能力使其成为对话语言模型中的佼佼者。让我们深入了解 ChatGLM-6B 的技术特点,探索它在对话模型中的创新之处。 GLM: 自回归空白填充的多任务预训练语言模型 ChatGLM-6B 技…...
函数递归所应满足的条件
1.递归的概念 递归是学习C语⾔函数绕不开的⼀个话题,那什么是递归呢? 递归其实是⼀种解决问题的⽅法,在C语⾔中,递归就是函数⾃⼰调⽤⾃⼰。 递归的思想: 把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但…...

Python入职某新员工大量使用Lambda表达式,却被老员工喷是屎山
Python中Lambda表达式是一种简洁而强大的特性,其在开发中的使用优缺点明显,需要根据具体场景权衡取舍。 Lambda表达式的优点之一是它的紧凑语法,适用于一些短小而简单的函数。这种形式使得代码更为精炼,特别在一些函数式编程场景中,Lambda表达式可以提高代码的表达力。此外…...
Android Bitmap保存成至手机图片文件,Kotlin
Android Bitmap保存成至手机图片文件,Kotlin fun saveBitmap(name: String?, bm: Bitmap) {val savePath Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString()if (!Files.exists(Paths.get(savePath))) {Log.d("保存文…...

frp V0.52.3 搭建
下载 https://github.com/fatedier/frp/releases/ 此版本暂时没有windows的,想在windows使用请下载v0.52.2 简易搭建 frps.toml的配置文件,以下12000、8500需要在云服务器中的防火墙中开放tcp # bindPort为frps和frpc通信的端口,需要在防…...
最近数据分析面试的一点感悟...
我是阿粥,也是小z 最近面了不少应届的同学(数据分析岗位),颇有感触,与各位分享。 简历可以润色,但要适度 运用一些原则,如STAR法则,让简历逻辑更清晰,条块分明࿰…...

ZYNQ_project:IIC_EEPROM
EEPROM简介: EEPROM(Electrically Erasable Progammable Read Only Memory, E2PROM)是指带电可擦可编程只读存 储器,是一种常用的非易失性存储器(掉电数据不丢失), E2PROM 有多种类型的产品,我…...
Leetcode 2940. Find Building Where Alice and Bob Can Meet
Leetcode 2940. Find Building Where Alice and Bob Can Meet 1. 解题思路2. 代码实现3. 算法优化 题目链接:2940. Find Building Where Alice and Bob Can Meet 1. 解题思路 这一题本质上又是限制条件下求极值的问题,算是我最不喜欢的题目类型之一吧…...

C++ 泛型编程,函数模版和类模版
1.泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础 就比如说活字印刷术,就是提供一个模具,然后根据模具来印刷出不同的字。 泛型编程跟着类似,提供一个模版,根据这…...

【封装UI组件库系列】封装Button图标组件
封装UI组件库系列第四篇封装Button按钮组件 🌟前言 🌟封装Button组件 1.分析封装组件所需支持的属性与事件 支持的属性: 支持的事件: 2.创建Button组件 🌟封装功能属性 type主题颜色 plain是否朴素 loading等…...

windows系统mobaxterm远程执行linux上ssh命令
命令如下 start "" "%~dp0\MobaXterm_Personal_23.4.exe" -newtab "sshpass -p root ssh root192.168.11.92 mkdir 33" -p 是密码 左边是用户名,右边是服务器ip 后面跟的是服务器上执行的命令 第一次执行的时候要设置mobaxt…...

debian 12 配置
1. 修改apt源 修改apt源为http版本 # 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释 deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware # deb-src http://mirrors.tuna.tsinghua.edu.cn/d…...

AIGC创作系统ChatGPT网站源码、支持最新GPT-4-Turbo模型、GPT-4图片对话能力+搭建部署教程
一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…...
Vue 中简易封装网络请求(Axios),包含请求拦截器和响应拦截器
Vue 中简易封装网络请求(Axios),包含请求拦截器和响应拦截器 axios简介 Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js Axios官方中文文档 特性 从浏览器创建 XMLHttpRequests从 node.js 创建 http 请求支…...

git提交报错error: failed to push some refs to ‘git url‘
1.产生错误原因 想把本地仓库提交到远程仓库,报错信息如下 git提交报错信息 error: src refspec master does not match any error: failed to push some refs to git url 错误原因: 我们在创建仓库的时候,都会勾选“使用Reamdme文件初始化…...
【Python】(自定义函数)模块的相对路径导入
是我以前写的老文章的升级版,本质上使用exec和sys.path实现相对路径导入。 RelativeImport: __version__1.1.0 __author__Ls_Janimport os import sys import inspectdef RelativeImport(module,*args):#模块导入module为模块所在路径(模块名不需要.py后…...

巧妙之中见真章:深入解析常用的创建型设计模式
设计模式之创建型设计模式详解 一、设计模式是什么?二、模板方法2.1、代码结构2.2、符合的设计原则2.3、如何扩展代码2.4、小结 三、观察者模式3.1、代码结构3.2、符合的设计原则3.3、如何扩展代码3.4、小结 四、策略模式4.1、代码结构4.2、符合的设计原则4.3、如何…...

Selenium切换窗口、框架和弹出框window、ifame、alert
一、切换窗口 #获取打开的多个窗口句柄 windows driver.window_handles #切换到当前最新打开的窗口 driver.switch_to.window(windows[-1]) #最大化浏览器 driver.maximize_window() #刷新当前页面 driver.refresh() 二、切换框架frame 如存在以下网页: <htm…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...