提交 bc28b775 编写于 作者: C CyC2018

auto commit

上级 02d2c579
......@@ -70,6 +70,18 @@
File、InputStream 和 OutputStream、Reader 和 Writer、Serializable、Socket 以及 NIO
# 编码实践
> [重构](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/重构.md)
> [编写可读代码的艺术](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/编写可读代码的艺术.md)
# 个人提升
> [黑客与画家](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/黑客与画家.md)
> [程序员的职业素养](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/程序员的职业素养.md)
# 资料下载
> [百度网盘](https://pan.baidu.com/s/1o9oD1s2#list/path=%2F)
......
<!-- GFM-TOC -->
* [译者序](#译者序)
* [前言](#前言)
* [专业主义](#专业主义)
* [说不](#说不)
* [说是](#说是)
* [编程](#编程)
* [测试驱动开发](#测试驱动开发)
* [练习](#练习)
<!-- GFM-TOC -->
# 译者序
作者 Bob 大叔,著有《敏捷软件开发:原则、模式与实践》《代码整洁之道》《程序员的职业素养》
对于解决问题,重要的不是问题本身,而是解决问题的方式、步骤以及反思的深度,这就是职业素养。
# 前言
由于管理者面临很大的财务和政治压力,他们无视技术人员发出的危险警告,抱着侥幸心理发射了“挑战者”航天飞机,最终导致航天飞机在高空爆炸。虽说技术人员已经做很多事情去阻止这次发射,但是并不是说做了所有他们能做的,例如他们就没有打电话去新闻台揭露此次发射的危险性。
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics/bccb799f-56e2-4356-95f0-a9ea05b0de2a.jpg)
# 专业主义
专业主义不仅意味着荣誉和骄傲,也意味着责任。
作者在自己开发的一个电话线路故障检测系统时,因为赶着在到期日交付新功能,没有对整个系统进行测试,导致了一个 bug 的出现。作者认为这是一种不负责任的表现,也就是不专业的表现。
代码中难免有 bug,但是不意味着你不用为 bug 负责。
有人把 QA 当成查 bug 的机器,等着 QA 发现 bug,而自己却不主动去发现 bug,这是不对的。
为了保证代码的正确性,需要写一些随时可以运行的单元测试,并且不断地去运行它们。
软件项目的根本原则 - 易于修改。为了让自己的软件易于修改,需要经常去修改它!
单元测试可以让程序员更有自信去修改代码,因为它会让人确信修改地是否正确。
专业素养包括以下内容:
1. 坚持学习
2. 每天练习一些简单的编程题目
3. 与他们合作,从彼此身上学到更多东西
4. 交流。通过交流表达自己的思想,发现自己的不足以及与别人思想上的差异。
5. 了解业务领域,找几本相关领域的书看。
6. 与雇主 / 客户保持一致
7. 谦逊,因为他们知道自己也可能犯错。
# 说不
对一些不合理的要求,不能只是说“试试看”,这会被当成是接受了这个要求。如果自己深知这种不合理的要求会导致严重的后果,就不能任由它发展下去,对这一要求说“不”。
说“不”时要用最详细的细节来说明,而不是烦躁地反驳。
当要求明显不合理时,可以协商一些双方都能接受的方案。
"有可能写出好代码吗" 这篇博文,讲述了一次开发一家零售商为了在"黑色星期五"能够让顾客看到商品信息商店信息以及发布优惠码等功能的 ipone 应用,最开始零售商的经理承诺说只要写个硬编码的应用就行了,但是客户所要的任何一项功能,总比它最开始时所说的要复杂许多。面对时间的紧急,客户方的不配合,以及需求的变更,让博文作者写了一堆很烂的代码,而他自己确实对那些设计模式等高级开发技术是非常热衷的。所以他认为在实际的开发中因为客户的需求等因素,很难写作自己想写的代码。Bob 认为,上面那篇博文的作者才是此次开发过程碰到的那些问题的负责人,因为他没有对那些不合理的要求说不。
# 说是
做出承诺的是三个步骤:口头上答应,放在心里,付诸行动。
缺乏承诺的词语:
1. 需要:我需要把这事做完;
2. 希望:希望今天我能完成任务;
3. 让我们:让我们把这件事做完。
缺乏承诺的人不会把重点放在自己身上,也不会明确说明一件事情的截止日期。真正的承诺是这样的“我会在...之前...”
对自己无法完全掌握的事,不要轻易承诺。
发现自己无法做到承诺,立马去调整别人对你的预期。
“试试看”说明自己对承诺的内容有点顾忌,因此不要用试试看来作为一种承诺。如果不能确信自己能达到承诺的内容而随便承诺,那么最后往往会产生严重的后果。
# 编程
要精通一项技术,要对该技术具备“信心”和“感知能力”。
状态不好的时候最好别写代码。状态不好分为:疲劳,比如熬夜;焦虑,因为外界的事情让自己不能安心下来,这个时候应该下调整好再进行编程。
进入流状态的感觉很好,觉得可以做很多事,但是流状态其实是一种浅层冥想,思维能力会下降,因此要避免进入流状态。
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics/f0321ed1-fa93-460e-951b-4239fef819f3.jpg)
流状态的特点是隔绝沟通,而结对编程能够打破这种隔绝,因此结对编程能够防止进入流状态。
当自己没法继续编程时,结对编程是个好选择,结对编程时,大脑和身体会有化学变化,也就是说思维方式会改变,有助于突破当前的思维阻塞状态。
听音乐时音乐会占用一部分脑力资源,因此会导致效率下降,但是这不是绝对。
多输入一些创造性的内容,比如科幻小说等,自己的创造力也会得到提升。
调试时间也很宝贵,测试驱动开发能够降低调试的时间。
编码和跑马拉松一样,无法全程以最快的速度冲刺,只能通过保持体力和维持节奏来取得胜利。因此要保持好自己的节奏,在疲劳的时候适当休息。
定期做进度衡量,用乐观预估、标准预估和悲观预估这三个时间点。
如果自己预估的进度赶不上截止时间,不要答应在截止时间去完成,因为你知道根据自己的预估这是不可能的。
任务完成的标准通常用一个自动化的验收测试来定义。
帮助他人也会给自己带来好处,在帮助别人的时候不要让自己看起来很仓促,就像是在随便应付。一般帮助别人不需要特别多的时间。也要学会请求别人的帮助,因为自己不可能是万能的,通过别人的帮助能更好的解决问题。
# 测试驱动开发
TDD 的运行周期很短,可能一两分钟就可以运行一次程序,短周期可以快速得到反馈,反馈在开发中特别重要,一方面是提高程序员编码的激情,另一方面是能够及早发现 bug,而 bug 越早发现修改地代价也越低。
TDD 三法则:
1. 先写测试;
2. 在一个单元测试失败的时候,不要再写测试代码;
3. 产品代码能恰好通过当前失败的单元测试即可,不要多写。
TDD 优点
1. 确定性:确定当前系统是否正确;
2. 信心:程序员更有信息去修改混乱的代码,因为通过单元测试可以知道是否修改地正确;
3. 文档:单元测试是底层设计细节的文档,通过阅读单元测试能够知道程序的运行方式;
# 练习
卡塔在武术里面是一套设计好的招式,该招式模拟真实搏斗场景,习武者不断训练来让身体熟悉该招式,从而做到纯熟。编程卡塔是一套编程过程敲击鼠标和键盘的动作,练习者不是为了解决真正的问题,因为已经知道了解决方案,而是练习解决这个问题所需要的动作和决策。
瓦萨即使两人对练的卡塔。在编程领域,可以是一个人写单元测试,另一个人写程序通过单元测试。比如,一个人实现排序算法,写测试的人可以很容易地限制速度和内存,给同伴施压。
<!-- GFM-TOC -->
* [第 1 章 可读性的重要性](#第-1-章-可读性的重要性)
* [第 2 章 用名字表达代码含义](#第-2-章-用名字表达代码含义)
* [第 3 章 名字不能带来歧义](#第-3-章-名字不能带来歧义)
* [第 4 章 良好的代码风格](#第-4-章-良好的代码风格)
* [第 5 章 编写注释](#第-5-章-编写注释)
* [第 6 章 如何编写注释](#第-6-章-如何编写注释)
* [第 7 章 提高控制流的可读性](#第-7-章-提高控制流的可读性)
* [第 8 章 拆分长表达式](#第-8-章-拆分长表达式)
* [第 9 章 变量与可读性](#第-9-章-变量与可读性)
* [第 10 章 抽取函数](#第-10-章-抽取函数)
* [第 11 章 一次只做一件事](#第-11-章-一次只做一件事)
* [第 12 章 用自然语言表述代码](#第-12-章-用自然语言表述代码)
* [第 13 章 减少代码量](#第-13-章-减少代码量)
<!-- GFM-TOC -->
# 第 1 章 可读性的重要性
编程有很大一部分时间是在阅读代码,不仅要阅读自己的代码,而且要阅读别人的代码。因此,可读性良好的代码能够大大提高编程效率。
可读性良好的代码往往会让代码架构更好,因为程序员更愿意去修改这部分代码,而且也更容易修改。
只有在核心领域为了效率才可以放弃可读性,否则可读性是第一位。
# 第 2 章 用名字表达代码含义
一些比较有表达力的单词:
| 单词 | 可替代单词 |
| --- | --- |
| send | deliver、dispatch、announce、distribute、route |
| find | search、extract、locate、recover |
| start| launch、create、begin、open|
|make|create、set up、build、generate、compose、add、new|
使用 i、j、k 作为循环迭代器不总是好的,为迭代器添加更有表达力的名字会更好,比如 user_i、member_i。因为循环层次越多,代码越难理解,有表达力的迭代器名字可读性会更高
为名字添加形容词等信息能让名字更具有表达力,但是名字也会变长。名字长短的准则是:作用域越大,名字越长。因此只有在短作用域才能使用一些简单名字。
# 第 3 章 名字不能带来歧义
起完名字要思考一下别人会对这个名字有何解读,会不会误解了原本想表达的含义。
用 min、max 表示数量范围;
用 first、last 表示访问空间的包含范围,begin、end 表示访问空间的排除范围,即 end 不包含尾部。
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics/26772ecc-a3e3-4ab7-a46f-8b4656990c27.jpg)
布尔相关的命名加上 is、can、should、has 等前缀。
# 第 4 章 良好的代码风格
适当的空行和缩进
排列整齐的注释:
```
int a = 1; // 注释
int b = 11; // 注释
int c = 111; // 注释
```
语句顺序不能随意,比如与 html 表单相关联的变量的赋值应该和表单在 html 中的顺序一致;
把相关的代码按块组织放在一起。
# 第 5 章 编写注释
阅读代码首先会注意到注释,如果注释没太大作用,那么就会浪费代码阅读的时间。那些能直接看出含义的代码不需要写注释,特别是并不需要为每个方法都加上注释,比如那些简单的 getter 和 setter 方法,为这些方法写注释反而让代码可读性更差。
不能因为有注释就随便起个名字,而是争取起个好名字而不写注释。
可以用注释来记录采用当前解决办法的思考过程,从而让读者更容易理解代码。
注释用来提醒一些特殊情况。
用 TODO 等做标记:
| 标记 | 用法 |
|---|---|
|TODO| 待做 |
|FIXME| 待修复 |
|HACH| 粗糙的解决方案 |
|XXX| 危险!这里有重要的问题 |
# 第 6 章 如何编写注释
尽量简洁明了:
```
// The first String is student's name
// The Second Integer is student's score
Map<String, Integer> scoreMap = new HashMap<>();
```
```
// Student' name -> Student's score
Map<String, Integer> scoreMap = new HashMap<>();
```
添加测试用例来说明:
```
//...
// Example: add(1, 2), return 3
int add(int x, int y) {
return x + y;
}
```
在很复杂的函数调用中对每个参数标上名字:
```
int a = 1;
int b = 2;
int num = add(\* x = *\ a, \* y = *\ b);
```
使用专业名词来缩短概念上的解释,比如用设计模式名来说明代码。
# 第 7 章 提高控制流的可读性
条件表达式中,左侧是变量,右侧是常数。比如下面第一个语句正确:
```
if(len < 10)
if(10 > len)
```
if / else 条件语句,先处理以下逻辑:① 正逻辑;② 关键逻辑;③:简单逻辑
```
if(a == b) {
// 正逻辑
} else{
// 反逻辑
}
```
只有在逻辑简单的情况下使用 ? : 三目运算符来使代码更紧凑,否则应该拆分成 if / else;
do / while 的条件放在后面,不够简单明了,并且会有一些迷惑的地方,最好使用 while 来代替。
如果只有一个 goto 目标,那么 goto 尚且还能接受,但是过于复杂的 goto 会让代码可读性特别差,应该避免使用 goto。
在嵌套的循环中,用一些 return 语句往往能减少嵌套的层数。
# 第 8 章 拆分长表达式
长表达式的可读性很差,可以引入一些解释变量从而拆分表达式:
```
if line.split(':')[0].strip() == "root":
...
```
```
username = line.split(':')[0].strip()
if username == "root":
...
```
使用摩根定理简化一些逻辑表达式:
```
if(!a && !b) {
...
}
```
```
if(a || b) {
...
}
```
# 第 9 章 变量与可读性
去除控制流变量。在循环中通过 break 或者 return 可以减少控制流变量的使用。
```
boolean done = false;
while(/* condition */ && !done) {
...
if(...) {
done = true;
continue;
}
}
```
```
while(/* condition */) {
...
if(...) {
break;
}
}
```
减小变量作用域。作用域越小,越容易定位到变量所有使用的地方。
JavaScript 可以用闭包减小作用域。以下代码中 submit_form 是函数变量,submitted 变量控制函数不会被提交两次。第一个实现中 submitted 是全局变量,第二个实现把 submitted 放到匿名函数中,从而限制了起作用域范围。
```
submitted = false;
var submit_form = function(form_name) {
if(submitted) {
return;
}
submitted = true;
};
```
```
var submit_form = (function() {
var submitted = false;
return function(form_name) {
if(submitted) {
return;
}
submitted = true;
}
}()); // () 使得外层匿名函数立即执行
```
JavaScript 中没有用 var 声明的变量都是全局变量,而全局变量很容易造成迷惑,因此应当总是用 var 来声明变量。
变量定义的位置应当离它使用的位置最近。
**实例解析**
在一个网页中有以下文本输入字段:
```
<input type = "text" id = "input1" value = "a">
<input type = "text" id = "input2" value = "b">
<input type = "text" id = "input3" value = "">
<input type = "text" id = "input4" value = "d">
```
现在要接受一个字符串并把它放到第一个空的 input 字段中,初始实现如下:
```
var setFirstEmptyInput = function(new_alue) {
var found = false;
var i = 1;
var elem = document.getElementById('input' + i);
while(elem != null) {
if(elem.value === '') {
found = true;
break;
}
i++;
elem = document.getElementById('input' + i);
}
if(found) elem.value = new_value;
return elem;
}
```
以上实现有以下问题:
- found 可以去除;
- elem 作用域过大;
- 可以用 for 循环代替 while 循环;
```
var setFirstEmptyInput = function(new_value) {
for(var i = 1; true; i++) {
var elem = document.getElementById('input' + i);
if(elem === null) {
return null;
}
if(elem.value === '') {
elem.value = new_value;
return elem;
}
}
};
```
# 第 10 章 抽取函数
工程学就是把大问题拆分成小问题再把这些问题的解决方案放回一起。
首先应该明确一个函数的高层次目标,然后对于不是直接为了这个目标工作的代码,抽取出来放到独立的函数中。
介绍性的代码:
```
int findClostElement(int[] arr) {
int clostIdx;
int clostDist = Interger.MAX_VALUE;
for(int i = 0; i < arr.length; i++) {
int x = ...;
int y = ...;
int z = ...;
int value = x * y * z;
int dist = Math.sqrt(Math.pow(value, 2), Math.pow(arr[i], 2));
if(dist < clostDist) {
clostIdx = i;
clostDist = value;
}
}
return clostIdx;
}
```
以上代码中循环部分主要计算距离,这部分不属于代码高层次目标,高层次目标是寻找最小距离的值,因此可以把这部分代替提取到独立的函数中。这样做也带来一个额外的好处有:可以单独进行测试、可以快速找到程序错误并修改。
```
public int findClostElement(int[] arr) {
int clostIdx;
int clostDist = Interger.MAX_VALUE;
for(int i = 0; i < arr.length; i++) {
int dist = computDist(arr, i);
if(dist < clostDist) {
clostIdx = i;
clostDist = value;
}
}
return clostIdx;
}
```
并不是函数抽取的越多越好,如果抽取过多,在阅读代码的时候可能需要不断跳来跳去。只有在当前函数不需要去了解某一块代码细节而能够表达其内容时,把这块代码抽取成子函数才是好的。
函数抽取也用于减小代码的冗余。
# 第 11 章 一次只做一件事
只做一件事的代码很容易让人知道其要做的事;
基本流程:列出代码所做的所有任务;把每个任务拆分到不同的函数,或者不同的段落。
# 第 12 章 用自然语言表述代码
先用自然语言书写代码逻辑,也就是伪代码,然后再写代码,这样代码逻辑会更清晰。
# 第 13 章 减少代码量
不要过度设计,编码过程会有很多变化,过度设计的内容到最后往往是无用的。
多用标准库实现。
此差异已折叠。
<!-- GFM-TOC -->
* [译者序](#译者序)
* [为什么书呆子不受欢迎](#为什么书呆子不受欢迎)
* [黑客与画家](#黑客与画家)
* [不能说的话](#不能说的话)
* [另一条路](#另一条路)
* [设计与研究](#设计与研究)
<!-- GFM-TOC -->
# 译者序
作者:美国互联网创业之父
最开始 hack 指的是难题的解决办法,那些最能干的人被成为 hacker。黑客包含以下三个特点:好玩、高智商、探索精神。黑客的核心价值观:分享、开放、民主、计算机的自由使用、进步。因此黑客指的是那些专家级程序员。
由于黑客常常入侵系统,因此被与那些破坏计算机系统的人联系起来。然而破坏计算机系统的人称为骇客,真正的黑客不这么做,反而是让世界变得更好。
# 为什么书呆子不受欢迎
作者认为,“书呆子”和“高智商”正相关,而“书呆子”和“受欢迎”负相关。
提出问题:既然书呆子智商高,那么为什么不想出受欢迎的方法?因为要想受欢迎,需要投入很大的时间精力去塑造个人魅力。而书呆子没有那么多的时间和精力,相比于受欢迎,他们更在乎其它更有趣的事,比如设计奇妙的火箭等。
欺负书呆子的原因:
- 青少年的孩子更加残忍,书呆子不受欢迎意味着被歧视和受欺负。
> 11 岁以前,小孩的生活由家长主导,其他孩子的影响有限。 孩子们不是不关心小学里其他同学的想法,但是后者不具有决定性影响。小学毕业以后,这种情形开始发生变化。到了 11 岁左右,孩子们逐渐把家庭生活当作寻常事了。 他们在同伴中开辟了一个新的世界,并认为那个世界才是重要的,比家里的世界更重要。实际上,如果他们在家里与父母发生冲突, 反而能在那个新的世界中挣得面子,而他们也确实更在乎那个世界。但是,问题在于,孩子们自己创造出来的世界是一个非常原始的世界。青少年在心理上还没有摆脱儿童状态, 许多人都会残忍地对待他人。他们折磨书呆子的原因就像拔掉一条蜘蛛腿一样,觉得很好玩。在一个人产生良知之前,折磨就是一种娱乐。
- 为了凸显自己。
> 在任何社会等级制度中,那些对自己没自信的人就会通过虐待他们眼中的下等人来突显自己的身份。我已经意识到,正是因为这个原因,在美国社会中底层白人是对待黑人最残酷的群体。最受欢迎的孩子并不欺负书呆子,他们不需要靠踩在书呆子身上来垫高自己。大部分的欺负来自处于下一等级的学生,那些焦虑的中间层。
- 为了和受欢迎的人结成同盟。
> 没有什么比一个共同的敌人更能使得人们团结起来了。这就好比一个政客,他想让选民忘记糟糕的国内局势,方法就是为国家找出一个敌人,哪怕敌人并不真的存在,他也可以创造一个出来。一群人在一起,挑出一个书呆子,居高临下地欺负他,就会把彼此联系起来。一起攻击一个外人,所有人因此就都成了自己人。这就是为什么最恶劣的以强凌弱的事件都与团体有关的原因。随便找一个书呆子,他都会告诉你,一群人的虐待比一个人的虐待残酷得多。
学校老师的只是把教书当成工作。
> 公立学校的老师很像监狱的狱卒。看管监狱的人主要关心的是犯人都待在自己应该待的位置。然后,让犯人有东西吃,尽可能不要发生斗殴和伤害事件,这就可以了。
到社会之后,书呆子会被友好对待,因为他们做的事能够产生更多的影响,而社会正需要这些影响。
成年人把孩子在学校受苦受难归结为青春期的激素影响,但是实际上这是学校这种体制的问题,学校只是把孩子圈养起来,不让他们到处乱跑,然而学校内部存在着很多残忍。
过去的孩子更早投入社会,也更能够真正学会本领。
> 过去的社会中,青少年扮演着一个更积极的角色。工业化时代到来前,青少年都是某种形式的学徒,不是在某个作坊,就是在某个农庄,甚至在某艘军舰上。他们不会被扔到一旁,创造自己的小社会。他们是成年人社会的低级成员。以前的青少年似乎也更尊敬成年人,因为成年人都是看得见的专家,会传授他们所要学习的技能。如今的大多数青少年,对他们的家长在遥远的办公室所从事的工作几乎一无所知。他们看不到学校作业与未来走上社会后从事的工作有何联系。
学校的使命是教书育人,而并没有外界的压力去监督他们这么做,所以老师和学生双方往往都是敷衍了事。
没有外在的对手,孩子们就互相把对方当作对手。
成年人贬低了很多美好的东西,比如“人格”。
> 许多书呆子可能都与我一样,直到高中毕业多年后,才去读中学里的指定读物。但是,我错过的绝不仅仅只是几本书而已。我对许多美好的字眼都嗤之以鼻,比如“人格”、“正直”,因为成年人贬低了这些词。在他们嘴里,这些词似乎都是同一个意思——“听话”。一些孩子因为具备所谓的“人格”和“正直”,而受到夸奖,可是他们不是呆得像一头大笨牛,就是轻浮得像一个不动脑筋的吹牛者。如果“人格”和“正直”就是这种样子,我宁愿不要它们。
# 黑客与画家
黑客和画家一样,都是艺术家,都在进行创作。
黑客比较喜欢脚本语言那种可以动态扩展,并且可以像铅笔画画一样修修改改。
> 编程语言首要的特性应该是允许动态扩展(malleable)。编程语言是用来帮助思考程序的,而不是用来表达你已经想好的程序。它应该是一支铅笔,而不是一支钢笔。如果大家都像学校教的那样编程,那么静态类型(static typing)是一个不错的概念。但是,我认识的黑客,没有一个人喜欢用静态类型语言编程。我们需要的是一种可以随意涂抹、擦擦改改的语言,我们不想正襟危坐,把一个盛满各种变量类型的茶杯,小心翼翼放在自己的膝盖上,为了与一丝不苟的编译器大婶交谈,努力地挑选词语,确保变量类型匹配,好让自己显得礼貌又周到。
编码并不是科学研究,因此数学家不一定写一手好代码。
> 创作者不同于科学家,明白这一点有很多好处。除了不用为静态类型烦恼以外,还可以免去另一个折磨科学家的难题,那就是“对数学的妒忌”。科学界的每一个人,暗地里都相信数学家比自己聪明。我觉得,数学家自己也相信这一点。最后的结果就是科学家往往会把自己的工作尽可能弄得看上去像数学。对于物理学这样的领域,这可能不会有太大不良影响。但是,你越往自然科学的方向发展,它就越成为一个严重的问题。
黑客为了能做自己喜欢做的事并且可以养活自己,往往白天工作晚上做自己的事。
热爱编程的人不可避免的会开发自己的项目。
> 我们面试程序员的时候,主要关注的事情就是业余时间他们写了什么软件。因为如果你不爱一件事,你不可能把它做得真正优秀,要是你很热爱编程,你就不可避免地会开发你自己的项目。
黑客不是机械工作的技术工人,而是一位创作者。
> 如果黑客只是一个负责实现领导意志的技术工人,职责就是根据规格说明书写出代码,那么他其实与一个挖水沟的工人是一样的,从这头挖到那头,仅此而已。但是,如果黑客是一个创作者,他从事的就不是机械性的工作,他必须具备灵感。
# 不能说的话
流行的道德观念虽然在后世看来会是荒诞的,但是如果在现今去违背这些道德观念,说一些不合适的言论,会让自己处于麻烦之中。
自己会深深赞同自己的观点,并且确信会得到别人的认同,很可能是,这些观点是别人给自己灌输的,别人说什么,自己就相信什么。
不能说的话往往是正确的话,因为具有争议性,会让某些人暴跳如雷。
那些不一定正确,极富争议性的言论被成为“异端邪说“,人们常常会给它们加上标签,目的是为了封杀它们。
> 亵渎神明、冒犯圣灵、异端都是西方历史上常见的标签,当代的标签则是有伤风化、不得体、破坏国家利益等。
禁忌的推崇者是那些处于中等阶层的人,他们不像高等阶层那样有足够自信保护自己的利益,又不同于低等阶层,他们有实力去推行。
流行观点的第一批追逐者是为了让自己与众不同,而第二批则是怕自己与众不同。
科学家往往需要打破流行观点,从而取得突破。
如果因为说了某些话而让干扰了自己的生活,最好还是别说。
> 这时你要明白,自由思考比畅所欲言更重要。如果你感到一定要跟那些人辩个明白,绝不咽下这口气,一定要把话说清楚,结果很可能是从此你再也无法自由理性地思考了。我认为这样做不可取,更好的方法是在思想和言论之间划一条明确的界线。在心里无所不想,但是不一定要说出来。我就鼓励自己在心里默默思考那些最无法无天的想法。你的思想是一个地下组织,绝不要把那里发生的事情一股脑说给外人听。“格斗俱乐部”的第一条规则,就是不要提到格斗俱乐部。
当狂热分子质问你的观点时,最好是说自己还没想好。
人们会误认为自己思想开放,但是他们心里早就有一根界限,认为什么是正确的什么是错误的。
更深入的认识自己。
> 儿童精疲力竭时,可能会大发脾气,因为他不知道为了什么;成年人则会了解是个人的身体状况问题,与外界无关,说一句“没关系,我只是累了”。
# 另一条路
互联网软件的优点
1. 不需要用户来管理系统,因此不需要专门的计算机知识,使用浏览器即可使用;
2. 需要的硬件资源很少,只需要能运行浏览器并且联网的设备就行;
3. 更方便,随时随地就可以使用;
4. 不用担心数据丢失,因为数据保存在云端;
5. 易于对软件进行升级,而用户可以在完全不知情的情况下继续使用;
6. 更容易的 bug 发现,更快的 bug 修改;
7. 有专门的技术人员管理服务器,因为更加安全;
bug 越快发现越好,而互联网软件因为发布周期短,用户反馈快,因此 bug 的发现也更快。
高级用户很乐意帮助寻找 bug。
> 因为软件发布以后,大多数 bug 都是罕见情况下才会发生的个案,受到影响的用户往往都是高级使用者,他们喜欢试验那些不常用的、难度大的操作。高级使用者对 bug 的容忍度比较高,尤其如果这些 bug 是在开发新功能的过程中引入的,而这些新功能又正是他们所需要的,他们就更能理解了。事实上,因为 bug 不多,你只有经过一些复杂的过程以后才会遇到它们,所以高级使用者往往因为发现了 bug 感到很得意。他们打电话给客服时,多半是一副胜利者的口吻,而不是怒气冲冲的样子,好像他们击败我们得分了一样。
好的技术支持是:客服人员和技术人员离得很近,并且当场修复 bug。
一个好的想法在实现过程中能引起更多想法,因此尽早去实现它比将它束之高阁能够带来更多好处。
不会购买软件的用户使用了盗版软件能够让软件更具有市场影响力。
> 一定数量的盗版对软件公司是有好处的。不管你的软件定价多少,有些用户永远都不会购买。如果这样的用户使用盗版,你并没有任何损失。事实上,你反而赚到了,因为你的软件现在多了一个用户,市场影响力就更大了一些,而这个用户可能毕业以后就会出钱购买你的软件。
# 设计与研究
设计追求的是好,而研究追求的是新。
用户要求和用户需求有区别,用户可能并不了解自己要什么,所以用户要求的内容往往不是用户真正需求的东西。
艺术设计与人为本,而科学研究追求简洁正确。比如数学家不会为了让读者更容易懂而采用一种更麻烦的证明,而是会选择最直接简洁的证明。编程语言也是与人为本的,因此编程语言也是通过设计产生的。
大理石做出的东西漂亮,但是制作过程却很难,无法不断的进行雕琢。编程语言也需要这种雕琢过程,如果一种编程语言像大理石一样,只是结果好看,那么它就不是一种好的编程语言。
设计软件应当尽快拿出原型来。就像画画一样,是先用几根线画出一个大致准确的轮廓,然后再逐步加工。
> 还有另一种软件设计思想,也许可以被称为“圣母玛丽亚”模式。它不要求尽快拿出原型,然后再逐步优化,它的观点是你应该等到完整的成品出来以后再一下子隆重地推向市场,就像圣母玛丽亚降临一样,哪怕整个过程漫长得像橄榄球运动员长途奔袭也没有关系。在互联网泡沫时期,无数创业公司因为相信了这种模式而自毁前程。
如果觉得做某件事很乏味,那么做出来的东西就会很乏味。快速的原型能够让程序员有更高的士气,从而让他们更有兴趣去实现。也可以理解为快速反馈很重要。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册