第四章. 模块性:清晰,简洁

1

Posted by conan | Posted in 读书笔记 | Posted on 08-09-2009

半年前看的《unix编程艺术》,笔记电子化。红色为重点部分,蓝色为个人废话。

模块化原则:模块间通过应用程序编程接口(API)──一组严密,定义良好的程序调用和数据结构来通信。

API标准:若试着用纯人类语言来描述设计(不摘录源代码),能否将事情说清楚?在编码前为API编写一段非正式书面描述是个好习惯。一些最有能力的开发者,一开始总是在定义接口,然后编写简要注释,对其进行描述,最后才编写代码──因为编写注释的过程就阐明了代码必须达到的目的,这种描述能够帮助组织思路,本身就是十分有用的模块说明。P85(编写简要注释这个习惯半年了还没有养成)

Hatton的经验数据表明,最佳模块长度为200~400逻辑行,大概400~800物理行。P87

紧凑性:若有经验的用户不需要操作手册,那么着个设计满足紧凑性。评测API的经验:若编程者要记忆的条目大于,则不大可能算是严格紧凑的。合理对待紧凑性,设计中尽量考虑,绝不随意抛弃。(七,人类短期记忆的极限)

正交性:每一个动作(无论是API调用,宏调用,还是语言运算)只改变一件事,不会影响其它。无论控制什么系统,改变一个属性的方法有且只有一个。

重构:改变代码的结构和组织,而不改变其外在行为,目的在于提高正交性。

SPOT:真理的单点性(Single Point of Truth)。不要重复自身。任何一个知识点在系统内部都应有一个唯一,明确,权威的表述。数据结构中也存在类似原则,“无垃圾,无混淆”。无垃圾是指数据结构(模型)应该最小化,无混淆是指在真实世界中绝对明确清晰的状态在模型中也应该同样明确清晰(不要重复自身这条基本在每本编程方法的书里都有)

自顶向下和自底向上:考量差异的方法。问一下设计是围绕主事件循环(常常具备与其非常接近的高级应用逻辑)组织,还是围绕主循环可能调用的所有操作的服务库组织代码。P95

模块化编码需要考虑的问题:

  • 有多少全局变量?全局变量是模块化的毒药,很容易使各模块轻率、混乱地互相泄露信息。
  • 模块大小是否在最佳范围?多了会产生长期的维护问题。若不知自己的最佳范围,则最好保守,坚持下限。
  • 模块内的单个函数是否太大?内部不要太复杂。
  • 代码是否有内部API?即可作为单元向其它人描述的函数调用集和数据结构集。且每个单元都封装了某一个层次的函数,不受其它代码影响。
  • API的入口点是否超过七个?哪个类有七个以上的方法?数据结构成员是否超过七个?
  • 每个模块的入口点数量如何分布?是否不均?有很多入口点的模块真的需要那么多入口点么?模块的复杂性往往和入口点数量的平房成正比。

Unix哲学基础

0

Posted by conan | Posted in 读书笔记 | Posted on 08-09-2009

半年前看的《unix编程艺术》,笔记电子化。

By Doug Mcllorg:

  1. 让每个程序就做好一件事,有新任务就重新开始,不要往原程序中加入新功能而搞的复杂。
  2. 假定每个程序的输出都会成为另一个程序的输入,哪怕那个程序还是未知的,输出中不要有无关信息干扰。避免使用严格的分栏格式和二进制格式输入。不要坚持使用交互式输入。
  3. 尽可能早的将设计和编译的软件投入试用,哪怕是操作系统。理想状况应该是几个星期内。对拙劣的代码别犹豫,扔掉重写。
  4. 优先使用工具而不是拙劣的帮助来减轻编程任务的负担。

重点:一个程序只做一件事,并做好,程序要能协作,程序要能处理文本流。

By Rob Pike:

  1. 程序耗费运行时间的地方无法判定,瓶颈经常出现在想不到的地方。证实瓶颈之前切勿急于找地方改代码。
  2. 估量:没有估量代码,尤其未找到最耗时的部分前不要去优化速度。
  3. 花哨算法在n很小的时候通常很慢,而n通常很小。花哨算法的常数复杂度很大,除非n一直很大,否则不要用。(拿不准就穷举)
  4. 花哨算法比简单算法更容易出现bug,难以实现。尽量简单算法配合简单数据结构。
  5. 数据压倒一切。编程的核心是数据结构,而不是算法。

Unix系统的十七个原则

  1. 模块原则:使用简单的结构拼合简单的部件。
  2. 清晰原则:清晰胜于机巧。优雅清晰的代码不仅不容易崩溃,而且更易于让后来的修改者立刻理解。
  3. 组合原则:设计时考虑拼接组合,互相通信。
  4. 分离原则:策略同机制分离,接口同引擎分离。如:前端策略,后端机制,设计分层。
  5. 简洁原则:设计要简洁,复杂度能低则低。
  6. 吝啬原则:除非确无他法,不要编写庞大的程序。(体积大复杂度高)
  7. 透明性原则:设计要可见,以便审查和调试。
  8. 健壮原则:健壮源于透明与简洁。(避免在代码中出现特例)
  9. 表示原则:把知识叠入数据以求逻辑质朴而健壮。(在设计中应主动将代码的复杂度转移到数据中去)
  10. 通俗原则:接口设计避免标新立异。
  11. 缄默原则:如果一个程序没什么好说的,就保持沉默。
  12. 补救原则:出现异常时马上退出并给出足量错误信息。宁为玉碎,不为瓦全。
  13. 经济原则:宁花程序一分,不花程序员一秒。(人少花时间,交给机器)
  14. 生成原则:避免手工hack,尽量编写程序去生成程序。如makefile生成器。
  15. 优化原则:雕琢前先得有原型,跑之前先学会走。(先制作原型系统,再精雕细琢,优化前先确保能用。 | 先求运行,再求正确,最后求快。)
  16. 多样原则:绝不相信所谓“不二法门”的断言。
  17. 扩展原则:设计着眼未来,未来总比预想快。

应用实例:

  • 只要可行,一切都应该作成与来源和目的无关的过滤器。
  • 数据应尽可能文本化。
  • 数据库部署和应用协议应尽可能文本化。
  • 复杂的前端(用户界面)和后端应该泾渭分明。
  • 如果有可能,用c语言编写前先用解释性语言搭建原型。
  • 当且仅当只用一门语言编程会提高程序复杂度时,混合语言编程比单一语言编程来的好。
  • 宽收严发。
  • 过滤时,不需要丢弃的信息决不丢。
  • 小就是美,在确保完成任务的基础上,程序的功能尽可能少。

天气预报改进之一

0

Posted by conan | Posted in 思考 | Posted on 08-06-2009

由于google天气预报总是月末让我手机访问某个网址,让没有开手机上网的我很不爽,于是找到linuxtoy上的飞信天气预报来尝试了一下
由于群发不怎么方便,现参考如下两个地址
http://blog.solrex.cn/articles/diy-free-weather-forecast-sms.html
做了一些改进
方案:
使用Solrex的命令行飞信工具作为最终发送工具
使用linuxtoy上 fangvv的方法抓取天气
参考数据与代码分离的思想,设计一个文本用来存放数据,使得增加群发对象和城市的时候不用修改代码
第一版文本格式:

[user]
13*********
[pass]
passwd
[city]
54161               长春
[to]
FetionId_1        自己
FetionId_2        nick
…..

[city]
59493           深圳
[to]
FetionId_1        nick

这个数据结构看起来很清晰,但是写起代码来判断比较多,最终awk脚本大概50多行代码。扫了一下unix编程艺术之后,参考.netrc做了如下改进:

第二版文本格式:

user    13*********
pass    passwd
city    54161           长春
to      FetionId_1        nick
to      FetionId_2        nick
…..
send    Yes

city    59493           深圳
to      FetionId_1        nick
…..
send    Yes

使用这个格式进行编码,awk脚本用了30行多点,代码如下

#!/usr/bin/awk -f
BEGIN{
SmsPath = “/home/conan/bin/weather/”
}
#支持注释
{
if (substr($1,1,1) == “#”)
next
}
#跳过空行
NF < 2{ next }
#设置用户名密码等信息
$1 != “to”{
Data[$1] = $2
}
#格式化群发列表
$1 == “to”{
if (Data["to"] == 0 )
Data["to"] = $2
else
Data["to"] = Data["to"] “,” $2
}
#获取天气并发送
Data["send"] == “Yes”{
Data["send"] = “No”
system(“wget -qnv -O ” Data["city"] ” http://wap.weather.com.cn/wap/” Data["city"] “/h24/”)
system(“sed -i -n ’15,31p’ ” Data["city"])
system(“sed -i ‘s/<[^<]*>//g’ ” Data["city"])
system(“sed -i /^$/d ” Data["city"])
#下面一行可以加上你要的内容
system(“sed -i ’1 i\**气象台为你预报’ ” Data["city"])
system(“sed -i ‘:a;N;s/\\n/ /g;ta’ ” Data["city"])
print (SmsPath “sendsms -vl -f ” Data["user"] ” -p ” Data["pass"] ” -t ” Data["to"] ” \”`cat ” Data["city"] “`\”")
system (“sleep 1″)
system (SmsPath “sendsms -vl -f ” Data["user"] ” -p ” Data["pass"] ” -t ” Data["to"] ” \”`cat ” Data["city"] “`\”")
print “——————–”
delete Data["to"]
delete Data["city"]
}

这版个人评价:不如上一版清晰明了,但是在人可以接收的情况下比较有利于编码。
由于google天气预报总是月末让我手机访问某个网址,让没有开手机上网的我很不爽,于是找到linuxtoy上的飞信天气预报来尝试了一下
由于群发不怎么方便,现参考如下两个地址
http://blog.solrex.cn/articles/diy-free-weather-forecast-sms.html
做了一些改进
方案:
使用Solrex的命令行飞信工具作为最终发送工具
使用linuxtoy上 fangvv的方法抓取天气
参考数据与代码分离的思想,设计一个文本用来存放数据,使得增加群发对象和城市的时候不用修改代码
第一版文本格式:

[user]
13*********
[pass]
passwd
[city]
54161               长春
[to]
FetionId_1        自己
FetionId_2        nick
…..

[city]
59493           深圳
[to]
FetionId_1        nick

这个数据结构看起来很清晰,但是写起代码来判断比较多,最终awk脚本大概50多行代码。扫了一下unix编程艺术之后,参考.netrc做了如下改进:

第二版文本格式:

user    13*********
pass    passwd
city    54161           长春
to      FetionId_1        nick
to      FetionId_2        nick
…..
send    Yes

city    59493           深圳
to      FetionId_1        nick
…..
send    Yes

使用这个格式进行编码,awk脚本用了30行多点,代码如下

#!/usr/bin/awk -f
BEGIN{
SmsPath = “/home/conan/bin/weather/”
}
#支持注释
{
if (substr($1,1,1) == “#”)
next
}
#跳过空行
NF < 2{ next }
#设置用户名密码等信息
$1 != “to”{
Data[$1] = $2
}
#格式化群发列表
$1 == “to”{
if (Data["to"] == 0 )
Data["to"] = $2
else
Data["to"] = Data["to"] “,” $2
}
#获取天气并发送
Data["send"] == “Yes”{
Data["send"] = “No”
system(“wget -qnv -O ” Data["city"] ” http://wap.weather.com.cn/wap/” Data["city"] “/h24/”)
system(“sed -i -n ’15,31p’ ” Data["city"])
system(“sed -i ‘s/<[^<]*>//g’ ” Data["city"])
system(“sed -i /^$/d ” Data["city"])
#下面一行可以加上你要的内容
system(“sed -i ’1 i\**气象台为你预报’ ” Data["city"])
system(“sed -i ‘:a;N;s/\\n/ /g;ta’ ” Data["city"])
print (SmsPath “sendsms -vl -f ” Data["user"] ” -p ” Data["pass"] ” -t ” Data["to"] ” \”`cat ” Data["city"] “`\”")
system (“sleep 1″)
system (SmsPath “sendsms -vl -f ” Data["user"] ” -p ” Data["pass"] ” -t ” Data["to"] ” \”`cat ” Data["city"] “`\”")
print “——————–”
delete Data["to"]
delete Data["city"]
}

这版个人评价:不如上一版清晰明了,但是在人可以接收的情况下比较有利于编码。