组合原则
简洁原则
透明性原则
经济原则
Keep It Simple,Stupid
看到该做的就去做,短期来看似乎是多做了,但从长期来看,这才是最佳捷径
软件设计和实现,应该是一门充满快乐的艺术,一种高水平的游戏
如果有足够多眼睛的关注,所有bug都无处藏身
Unix要繁荣,就必须采用吸纳低价而灵活的方案的诀窍,而不是去反对他们
318-327看完
软件有两种设计方式,一种设计得极其简洁,没有看得到的缺陷;一种设计得极其复杂,看不出缺陷,第一种难的多
要编写复杂软件又不至于一败涂地的唯一方法,就是用定义清晰的接口把若干简单模块组合起来,这样多数问题只会出现在局部
一些最有能力的开发者,一开始总是定义接口,然后编写简要注释,对其进行描述,最后才编写代码,因为编写注释的过程,就阐明了代码必须达到的目的
模块之间通过API接口通信,bug最少的最佳物理行在400到800,关于逻辑行和物理行见下面代码
a="我是一个物理行"
a="""我是一个逻辑行
因为我一条语句便跨越了2个物理行"""
就是一个设计能否装进大脑的特性: 用经验的用户通常需要操作手册吗?如果不需要,那么这个设计就是紧凑的
经验法则: 记忆的条目数大于七,则不太可能是紧凑的
任何操作均无副作用,只会改变一件事,而不会影响其他
体现了 单一职权原则 只做好一件事
正交性代码更容易复用
Don't repeat youself 当你修改重复部分时,你可能只是修改了一部分而非全部,非常危险
设计最好围绕一个强核心算法,就比如,grep围绕正则表达式
完美之道,不在无可增加,而在无可删减
设计方法有自顶向下和自底向上,unix采用自底向上
为了中和自顶向下和自底向上,我们需要胶合层,类似于分层的api,service,common层
而且胶合层应该尽量的薄,比如C语言,就是硬件和软件的胶合层,而C语言的风格就是尽量简洁,功能尽量少
API的入口点尽量不超过七个,简单的API比复杂API更好
5.1 文本化的重要性
文本文件/协议优于二进制流文件/协议,因为透明性(用户易读懂)
这一章就是一堆文本格式的介绍 RFC 822(电子邮件格式,包括 to,subject,cc等) 和XML
以及Unix文本格式的约定
看到 5.3 应用协议设计。 有点看不下去,好无聊,也看不太懂
看到490页,5.4应用协议元格式,这章完全讲协议啊,不看了
511~554
美在计算机科学中的地位,比在其他任何技术中心的地位都重要,因为软件太复杂了。美是低于复杂的终极武器。
可显性,仅仅做到不晦涩是不够的,还必须尽力做到有帮助,比如有意义的函数名称,或者出现故障时有用的日志信息,比如gcc编译时,打出来的一堆调试信息,易于帮助我们定位,哪里出了问题
透明性,就是我们一眼就知道,这个机器(或者代码)在干什么
编写透明、可显得系统而节省的精力,将来完全可能就是自己的财富
Window注册表,所有注册记录都储存在一个大文件中,这是不好的,大文件应该分为多个小文件
策略和机制应该分离,策略,就好比你玩家在游戏中的装备等数据,而服务器引擎则是机制
Freeciv这个游戏,其配置文件类似于windows注册表,但是它的配置文件只能由游戏开发者改动,这就避免了用户改动注册表,导致的注册表过长,运行速率过慢。
这个设计能行吗?
别人能读懂这个设计吗?
这个设计优雅吗?
这三个问题不是废话,优雅不是一种奢侈,这些品质对于减少bug和提高软件长期维护性是最基本的
最有效的方法很简单,就是不要再具体操作的代码上叠放太多抽象层??? 这也是为什么要有薄胶合层
优秀Unix代码的简洁依赖于严格自律和高水平技艺
6.2.2 为透明性和可显性
去欲望,少依恋,如实见
程序调用的最大静态深度是多少,如果大于四,就要当心
每个API的各个函数调用是否正交? 或者存在太多的特征标志?
是否存在一些顺手可用的关键数据结构或全局唯一的记录器
代码增加了特殊情况还是避免了特殊情况
透明性和避免过度保护
隐藏细节和无法访问细节有着重要区别(比如debug时能打出隐藏的日志)
良好程序应该有调试和探测开关
尽量实现可编辑的文本和二进制格式来回无损转换
CLI (comand line interface)命令行接口程序
6.2.5 透明性、故障诊断和故障恢复
透明的更容易恢复,也更健壮
6.3 可维护性
如果作者以外的其他人能顺利理解和修改软件,那么软件就是可维护的
Unix哲学:宁愿抛弃、重建代码也不愿修补哪些蹩脚的代码
可维护的两个重要实践:
选择简单的算法,“拿不准就穷举”,复杂的算法容易出bug,而且难以维护
还有就是,开发者手册,简略描述代码的关键数据结构和算法
Unix的艺术: 将大型程序分解成多个协作进程
做单件事并做好
shell执行,就是创建多个协作进程,并由管道连接
IPC 进程间通信
7.1
过早优化是万恶之源
线程不是降低而是提高了全局复杂度,因此,除非万不得已,尽量避免使用线程
一个程序划分为多个协程,也更利于安全性,只有较小的协程去执行特权指令
最简的就是最好的
专门程序,其实就是不和其他进程通信,只做自己的事情的程序
管道 就是 做单件事并做好
8.1 理解语言分类法
结构或用的数据格式文件(比如/etc/passwd) -> 微型语言(比如make) -> js -> Java
这些语言一定程度上是解释器,解释器范畴越大(即所需要的上下文越少)越通用(越靠右),
png没有透明性,sng却能够将png以json字符串的样式表现出来,所以具备了透明性
8.2.2 正则表达式
微语言的一种,极其简练却很有用
8.2.3 Glade
透明性和简单性
8.2.4 m4
宏命令: define 'OS' 'operation system'
但是谨慎用宏
8.2.5
图灵完备机
其实语言就是解释器?
最小立异原则
必须谨慎使用语法糖,以免造成的晦涩多于帮助
8.2.8 awk
unix下输入 info gawk,获取在线文档
程序员束手无策.... 只有跳脱代码,直起腰,仔细思考数据才是最好的行动,表达是编程的精髓(????表达力吗)
人类其实更善于观察数据,而不是推导控制流程(我觉得,人类是视觉动物)
所以,尽可能的把设计的复杂度从程序代码转移到数据,是个好办法
过程式语言 c,
说明式语言 sql
相对来说编译时,c更难分分析,sql更容易分析(我觉得,透明性),容易分析的语言,更容易找出错误
进行数据驱动编程(和面向对象编程不同)时,需要把代码和代码作用的数据结构划分清除,这样,在改变程序的逻辑时,只要编辑数据结构而不是代码,例子:ascii转译程序,只需要码表和map逻辑
9.2 专用代码的生成
9.2.1 ascii 代码
assic 用法屏幕,就是作者自己手写一整个表,然后让代码去打印这个表, 这样的话,要改输出格式,只要对这个手写的表进行操作就行啦,NB666
我草,这个真是屌爆了
而我们可以先把数据按以下格式存储在文档中
然后通过sed,生成对应的html列表
我草,太吊了
所有这些例子的借鉴之处都是一样的:
尽可能少干活;让数据塑造代码;依靠工具;把机制从策略中分离
建设性的懒惰是大师级程序员的基本美德之一
编码尽量往上推以最小化常数缺陷密度效应??????
积硅步,至千里
什么应是可配置的? 回答:全部
分离原则: 只要可能,就建立机制而把策略决定权交个用户
尽量用自动检测来减少配置开关的数量
让程序经济运行时设计者的任务,用户不应该看到优化开关
考虑一下问题:
能省掉这个功能吗
能否用无伤大雅的方式改变程序的常规行为,从而无需这个选项?
这个选项是否花哨没用?
增加一个开/关配置选项,就是使测试量加倍,既然在实践中从来没有人完成双倍测试量,那么实际影响,就是减少了特定配置获得的测试量
1 /ect
2 系统环境变量
3 用户主目录(LINUX中的~)的运行控制文件
4 用户环境变量
5 命令行参数
后者覆盖前者,越往后范围越小
rc 后缀,代表'运行控制(run control)'
其实运行控制文件和点文件(比如.netrc文件, .profile文件),我的理解就是配置文件,用来决定程序按照何种配置去运行
10.3 命令行选项
-a -ab代表组合-a和-b
10.5.1 从-a到-z的命令行选项
-a -all所有
-b buffer或者batch
-c command 命令(带参数)
-d debug 调试,偶尔delete
-e execute
-f file 代表输入文件,输出文件
-i interactive
-k keep或者kill
-l list
-n number
-o output
-p port
-q quite
-r recurse递归 reverse反向
-z zip启用压缩
看到881页
提供了了太多的选项,以至于用户手册过于复杂
解决办法就是提供-o选项,然后-o后面的参数视为配置文件的一行信息
我们所有的知识都来源于我们的感知
与其他程序通讯方式的前瞻性设计,这是什么鬼?????
最小立异原则: 如果可能,尽量允许用户将接口功能委派给熟悉的程序来完成,比如让用户选择自己的文本编辑器
互助式的接口组合使用
共生和委派策略来提高代码的复用度,并降低软件复杂度: 让用户选择自己的代理
如果不能委派,那么就效仿
简洁、表现力、透明、易用、脚本化
简洁: 可以用操作时间来衡量,键盘拼写比鼠标点击屏幕字符要快得多
表现力:可以触发相当广泛的行为,比如键盘可以单键,也可以组合键
易用性:和用户要记忆的东西成反比
接口透明度:所见即所得,中间结果也很容易看出来
脚本化: 接口更容易为其他程序所用
自动完成重复任务:
roguelike
更具表达力的语言意味着程序更短,bug更少。
过滤器原则
Postel原则 宽进严出
过滤时,不需要的信息也绝不丢弃
过滤时,绝不增加无用数据 比如不遵循格式的日期,空行
Cantrip模式
没有输入没有输出,启动时指定条件,具备很强的脚本能力
比如 clear rm命令
比较好的风格,交互用脚本语言写,然后内部调用cantrip程序
源模式
没有输入,输出只能在启动条件中控制,比如 ls
类编译器模式
没有输入也没有输出,但是报错时会发信息
,交互性较低,所以比较容易脚本化
引擎和接口分离模式
核心算法与接受用户命令,显示结果相分离
比如控制器,视图,模型
驱动/引擎组合
比如驱动可以是不同的网卡驱动,而网卡作为引擎,只有一个
客户端/服务器模式
配置者/执行者组合
比如fetchmail和fetchmailconf
fetchmailconf用于GUI
假脱机和守护进程
守护进程,轮询作业
CLI服务器模式
其实就是守护进程负责监听,然后fork进程处理输入,比如inetd
微语言模式
GUI前端,CLI微型语言后端比如数据库软件
要促进脚本化和管道线能力(参考第 7 章),最好就是尽可能地选择最简单的接口设计模式——牵扯环境因素最少、交互最少的模式。
既有脚本方式的接口,又有GUI方式的接口
CGI 浏览器内嵌程序,用来接受用户请求,并处理
喋喋不休的程序,其他程序很难理解,更难交互
程序每产生一行垃圾,用户可见的信息就少了一行。
信息内容应该符合最大惊奇原则——仅仅对偏离通常期望的情况详加说明。
过早优化乃万恶之源
Unix的经验告诉我们最主要的就是如何知道何时不去优化。其次,最有效的优化往往是优化之外的其它事情,如:清晰干净的设计。
程序员工具箱中最强大的优化技术就是不做优化。
如果有真凭实据证明应用程序运行缓慢,这时(仅当此时)才可以考虑优
最有效的代码优化方法就是保持代码短小简单
永远不要将核心数据结构和时间关键循环抛出缓存。
“小即是美”的建议比以往更有用,尤其是考虑到核心数据结构必须留在最快的缓存里。
三种常规的策略来减少时延,(a)对可以共享启动开销的事务进行批处理,(b)允许事务重叠,和(c)缓存。
一致性在简单情况下可以保障,所以我绝对不要用二进制缓存,用map做缓存就行了
13.1 谈谈复杂度
Unix程序员追求简单的激情,源自注重实效的事实:复杂度就是成本
更多行的代码意味着更多的 bug,而调试常常是开发中最昂贵、最耗时的部分。
Unix思想中的一个主题就是强调工具小巧锐利,设计从零开始,接口简单一致。
如果目标是抑制整体复杂度,最愿意牺牲的是什么地方?什么地方又最该被牺牲掉?
本章大多数的问题,良好品味和工程判断力要求,情况不同,则答案不同。
重要的是要培养斟酌每一个设计的习惯。
正如我们在讨论软件模块性之前的建议一样,复杂度的算盘必须打好。
偶然复杂度的产生是因为没有找到实现规定功能集合的最简方法。偶然复杂度可以由良好的设计或重新设计来去除。另一方面,选择复杂度,同某个期望的功能相关联,只能由改变工程的目标来去除。
要么简约主义,要么无所不能
程序要么小巧,要么庞大,中间道路行不通
所有真正有用的程序,都想变成瑞士军刀
只有实证了其他方法行不通时,才写庞大程序
攀爬学习曲线的一次性付出,得到的是更有效编写程序的能力;
精力也可以更多地放在设计层面而不是低层次的细节操作。
make用于文档转换,比如将html标签文件转换成易读的文本格式
看到1287
困难的是,语法正确的程序并不如期望的那样运行,解决办法是
透明设计性:内部数据的流向容易被人眼和简单工具审视
透明设计有利于防止bug,以及减轻运行期调试任务
make比较出名的工具是autoconf
profiler用来工具,用来检测性能
15章看完
不言之教,无为之益,天下希及之。
避免重新发明轮子的最有效方法是借用别人的设计和实现。换句话说,重用代码
Unix的经验是,养成良好的习惯,尝试通过最少的新发明,组合现有组件以形成原型,而非匆忙地编写独立的、只能使用一次的代码。
实际上,任何具有非平凡API的软件,如果无法深入肌理,甚至无法正确使用。
所以,开放源码”具有更深远的意义
设计最好的实践需要情感的投入,而不是冷漠无聊的过程。软件开发者,同其它任何类型的工匠和技师一样;他们想要成为艺术家,这并不是什么私密。
16.4
在开源世界的开发者,从来不会受最终期限的压迫,不会一闭眼、一拍脑门就发布软件。
Unix下工作,最管用的技能之一就是熟练地掌握将代码粘合在一起的各种方法,从而能够应用组合原则。
更一般地,阅读代码是为未来而投资。可以从中学到甚多——新技术、分解问题的新方法、不同的风格和手段。使用代码和学习代码都能得到有价值的回报。
写之前先读;培养阅读代码的习惯。
Unix标准,POSIX,主要是描述:系统调用,C函数库,shell语义等
标准化,其实就是为了可移植
标准定义的IETF哲学:“我们反对国王、总统和投票。我们信任大致的共识和可运行的代码”
请求意见稿(英语:Request for Comments,缩写:RFC),又翻译作意见征求,意见请求,请求评论[1]是由互联网工程任务组(IETF)发布的一系列备忘录。
我的理解,RFC也算是一种被提出来的规范,但是流行程度取决于广大开发者
补救糟糕的代码或设计,比起重新开始尝尝更费时费事。
Unix文化主张干脆拆毁重来。
先原型然后循环不断地测试和演进才是更好的方法。
良好的规格说明(我的理解是程序的说明文档)具有巨大的价值。
规格说明,就是一切? 所以规格说明是?需求文档?
标准,是DNA,我们根据DNA生成RNA,RNA即是根据标准生成的系统,比如Linux
可移植性,空间上,可移植到不同主机;时间上,几十年后仍然能用;时间上的可移植性甚至更重要
通常可以用autoconf来探查本地配置,从而解决可移植性问题
分离信息库和代码,我觉得和上面那个手工写的ASCII常量表有点类似
基于开放源码编程,而不是专用代码,这样能更好应对,接口迭代导致的不兼容
所见即所得(WYSIWYG)和 已标记为中心的工具(比如xml,html,和latex?) ,标记工具更容易设计格式,布局
unix手册,一般都有个BUGS部分,事无巨细地揭露软件的已知缺陷
文档类型定义(DTD,Document Type Definition)是一种特殊文档,和XML语法规则相关
看完
软件和性一样,越自由越好
数量多不会被认为是质量高。尤其是,决不要因为害怕别人看不懂而省略功能细节,决不要为了面子而不对存在的问题提出警示。不愿坦露问题才会损害信誉和用户,坦白了的问题则不会。
开源开发利用了这样的事实,甄别和修改 bug 的任务适合分解成多个并行的子任务——这和实现某个特殊算法不一样
尽早发布,经常发布
因此,精工细作,等一切完美了才发布的想法是要不得的。
努力选择唯一且容易键入的名称前缀
使用GNU自动工具
先测试再发布代码
比如,使用查找内存泄漏和运行期错误的软件;Electric Fence和Valgrind
拥有FAQ文件可以让你省很多力气。当关于项目的某个问题经常出现时,就加到FAQ文件中
发展良好的FAQ可以减少项目维护者一个数量级的负担,甚至更多。
看完了
预测未来的最好方法就是去创造未来
Unix 程序员在 30年风雨中学到最有经验的回应,就是回到最初的准则——优先从流、命名空间、进程等Unix基本抽象中得到更多效用,而不是增加新的东西。
更优秀解决方案的最危险敌人,就是一个现存的、足够优秀的代码库。
C语言缺乏抛出附带数据的命名异常的机制。因此,Unix API中的C函数用与众不同的的返回值(通常是-1或NULL)并设置全局变量errno来报告错误。[7]
交互设计也包含了大量坚实的真理,需要为每个Unix程序员所知道。
ioctl和fnctl比较尴尬
通过fcntl设置的都是当前进程如何访问设备或文件的访问控制属性,例如读、写、追加、非阻塞、加锁等,但并不设置文件或设备本身的属性,例如文件的读写权限、串口波特率等。
ioctl函数用于设置某些设备本身的属性,例如串口波特率、终端窗口大小,注意区分这两个函数的作用。
波特率即指一个单位时间内传输符号的个数。
现在得到的90%,比等不来的100%更有价值,他强调实现的健壮性和简单性
Unix传统是简单和空
一个人所拥有的的理念和素质,远比他所表现出来的专业技能重要的多????? 我不赞成,有一说一