├── src └── main │ └── java │ ├── chapter_11 │ ├── README.md │ ├── Example3.js │ ├── Example5.js │ ├── Example1.js │ ├── Example2.java │ ├── Example6.java │ └── Example4.java │ ├── chapter_12 │ ├── README.md │ ├── Example1.js │ ├── Example2.java │ └── Example3.java │ ├── chapter_14 │ ├── README.md │ ├── Example1.java │ ├── Example3.java │ ├── Example4.java │ ├── Example2.java │ └── access.log.txt │ ├── chapter_15 │ └── README.md │ ├── chapter_16 │ └── README.md │ ├── chapter_13 │ ├── README.md │ ├── Example2.js │ ├── Example1.java │ └── Example3.java │ ├── chapter_02 │ ├── README.md │ ├── Node.java │ ├── Example4.java │ ├── Coder.java │ ├── Example2.java │ ├── Example1.java │ └── Example3.java │ ├── chapter_10 │ ├── README.md │ ├── Example6.java │ ├── Example3.java │ ├── Example2.java │ ├── Example4.js │ ├── Example1.java │ ├── Example7.html │ └── Example5.java │ ├── chapter_05 │ ├── README.md │ ├── Example6.java │ ├── Example4.java │ ├── Example1.java │ ├── Example3.java │ ├── Example2.java │ └── Example5.java │ ├── chapter_09 │ ├── README.md │ ├── Example1.java │ ├── Example4.java │ ├── Example3.java │ ├── Example2.java │ ├── Example6.java │ └── Example5.js │ ├── chapter_07 │ ├── README.md │ ├── Example10.java │ ├── Example9.java │ ├── Example12.java │ ├── Example11.java │ └── Example8.java │ ├── chapter_04 │ ├── README.md │ ├── Example1.java │ ├── Example4.java │ ├── Example2.java │ ├── Example3.java │ └── Example5.java │ ├── chapter_06 │ ├── Example4.java │ ├── Example6.java │ ├── Example3.java │ ├── README.md │ ├── Example5.java │ ├── Example1.java │ ├── Example2.java │ └── Example7.java │ ├── chapter_03 │ ├── BinaryTree.java │ ├── Example5.java │ ├── Example1.java │ ├── Example4.java │ ├── Example6.java │ ├── README.md │ ├── Example2.java │ └── Example3.java │ └── chapter_08 │ ├── README.md │ ├── Example3.java │ ├── Example1.java │ ├── Example4.java │ ├── Example6.java │ ├── Example2.java │ └── Example5.java ├── README.md ├── pom.xml ├── LICENSE └── .gitignore /src/main/java/chapter_11/README.md: -------------------------------------------------------------------------------- 1 | # 抽取无关的代码 -------------------------------------------------------------------------------- /src/main/java/chapter_12/README.md: -------------------------------------------------------------------------------- 1 | # 一次只干一件事 -------------------------------------------------------------------------------- /src/main/java/chapter_14/README.md: -------------------------------------------------------------------------------- 1 | # 少写点代码 -------------------------------------------------------------------------------- /src/main/java/chapter_15/README.md: -------------------------------------------------------------------------------- 1 | # 测试和可读性 -------------------------------------------------------------------------------- /src/main/java/chapter_16/README.md: -------------------------------------------------------------------------------- 1 | # 你的最终考验 -------------------------------------------------------------------------------- /src/main/java/chapter_13/README.md: -------------------------------------------------------------------------------- 1 | # 把想法变成代码 2 | -------------------------------------------------------------------------------- /src/main/java/chapter_02/README.md: -------------------------------------------------------------------------------- 1 | # 写让人理解的代码 2 | 3 | 代码的写法应该使理解代码的人所需要的时间最小化。 -------------------------------------------------------------------------------- /src/main/java/chapter_10/README.md: -------------------------------------------------------------------------------- 1 | # 变量和可读性 2 | 3 | 1. 减少变量,通过立刻处理结果来消除中间变量。 4 | 2. 减少变量作用域,作用域越小越好。 5 | 3. 变量只写一次最好,只设置一次的变量会让代码变得更容易理解。 -------------------------------------------------------------------------------- /src/main/java/chapter_05/README.md: -------------------------------------------------------------------------------- 1 | # 写代码也需要审美? 2 | 3 | 1. 如果多个代码块做同样的事,尝试让它们有同样的剪影 4 | 2. 把代码块按照 “列” 对齐可以让代码更容易阅读 5 | 3. 如果一段代码中用到了 A、B、C,那么在使用它们的下方就应该保持顺序一致 6 | 4. 使用空行将大段的代码分为逻辑上的“段落” 7 | -------------------------------------------------------------------------------- /src/main/java/chapter_09/README.md: -------------------------------------------------------------------------------- 1 | # 拆分又臭又长的表达式 2 | 3 | 引入“解释变量”代替较长的子表达式,有三个好处: 4 | 5 | 1. 它把巨大的表达式拆分成一个小段 6 | 2. 通过简单的名字来描述一个子表达式,让代码文档化 7 | 3. 它帮助读者识别代码中重要的概念 8 | 9 | **用德摩根定理来操作逻辑表达式** -------------------------------------------------------------------------------- /src/main/java/chapter_07/README.md: -------------------------------------------------------------------------------- 1 | # 什么样的注释是好的 2 | 3 | ## 写出言简意赅的注释 4 | 5 | - 当像 “这里” 和 “it” 这样的代词可能指代多个事物时,避免使用它们 6 | - 尽量精确的描述方法行为 7 | - 在注释中用精心挑选的输入/输出例子进行说明 8 | - 声明代码的高层次意图,而非明显的细节 9 | - 用含义丰富的词来使注释更加简洁 -------------------------------------------------------------------------------- /src/main/java/chapter_10/Example6.java: -------------------------------------------------------------------------------- 1 | package chapter_10; 2 | 3 | /** 4 | * 只写一次的变量更好 5 | * 6 | * @author biezhi 7 | * @date 2018/7/25 8 | */ 9 | public class Example6 { 10 | 11 | public static final int NUM_THREADS = 10; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/chapter_04/README.md: -------------------------------------------------------------------------------- 1 | # 让人不会误解的名字 2 | 3 | 不会误解的名字是最好的名字——阅读代码的人应该理解你的本意,并且不会有其他的理解。 4 | 5 | - 表示上限和下限:max min 6 | - 表示包含的范围:first last 7 | - 表示包含、排除某个范围:begin end 8 | 9 | 命名一个 bool 类型的值,应该使用 is 、 has 这样的词来明确它所表达的含义。 10 | 避免使用反义的词,比如 disable。 11 | 12 | 要小心用户对特定词的期望。例如用户会认为 get 或 size 是一个轻量的方法。 -------------------------------------------------------------------------------- /src/main/java/chapter_05/Example6.java: -------------------------------------------------------------------------------- 1 | package chapter_05; 2 | 3 | /** 4 | * 个人风格与一致性 5 | * 6 | * @author biezhi 7 | * @date 2018/7/3 8 | */ 9 | public class Example6 { 10 | 11 | class Tigger { 12 | 13 | } 14 | 15 | // or 16 | 17 | class Coder 18 | { 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/chapter_02/Node.java: -------------------------------------------------------------------------------- 1 | package chapter_02; 2 | 3 | /** 4 | * @author biezhi 5 | * @date 2018/6/24 6 | */ 7 | public class Node { 8 | 9 | private Node next; 10 | 11 | public Node next() { 12 | return this.next; 13 | } 14 | 15 | public String data(){ 16 | return ""; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/chapter_04/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_04; 2 | 3 | /** 4 | * 截取字符串 5 | * 6 | * @author biezhi 7 | * @date 2018/6/27 8 | */ 9 | public class Example1 { 10 | 11 | private void truncate(String text, int maxChars) { 12 | 13 | } 14 | 15 | public void invoke() { 16 | this.truncate("hello world", 5); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/chapter_02/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_02; 2 | 3 | /** 4 | * @author biezhi 5 | * @date 2018/6/24 6 | */ 7 | public class Example4 { 8 | 9 | private int hash; 10 | private int c = 2018; 11 | 12 | public void calcHash() { 13 | // Fast version of "hash = (65599 * hash) + c" 14 | hash = (hash << 6) + (hash << 16) - hash + c; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/chapter_14/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_14; 2 | 3 | /** 4 | * 商店定位器 5 | * 6 | * @author biezhi 7 | * @date 2018/9/24 8 | */ 9 | public class Example1 { 10 | 11 | /** 12 | * 给某个用户实现一个“商店定位器” 13 | * 14 | * 对于任何给定用户的经纬度,找到距离该经纬度最近的商店。 15 | * 16 | * 1. 当位置处理国际日期分界线两侧的情况 17 | * 2. 接近北极或南极的位置 18 | * 3. 按 “每英里所跨经纬度” 不同,处理地球表面的曲度 19 | */ 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/chapter_04/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_04; 2 | 3 | /** 4 | * @author biezhi 5 | * @date 2018/6/27 6 | */ 7 | public class Example4 { 8 | 9 | boolean hasPassword = true; 10 | 11 | boolean useSSL = false; 12 | 13 | 14 | public int computeChinaFriends(String username) { 15 | // 访问数据库查询 username 的所有访客朋友,筛选计算出所有在中国的访问朋友,并计算个数返回 16 | return -1; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/chapter_07/Example10.java: -------------------------------------------------------------------------------- 1 | package chapter_07; 2 | 3 | /** 4 | * 用输入/输出例子来说明特殊情况 5 | * 6 | * @author biezhi 7 | * @date 2018/7/7 8 | */ 9 | public class Example10 { 10 | 11 | /** 12 | * 从 src 中移除以 chars 开头/结尾的字符 13 | * 14 | * Example: strip("abba/a/ba", "ab") return "/a/" 15 | */ 16 | public String strip(String src, String chars) { 17 | // logic 18 | return ""; 19 | } 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/chapter_02/Coder.java: -------------------------------------------------------------------------------- 1 | package chapter_02; 2 | 3 | /** 4 | * 皮卡丘 5 | * 6 | * @author biezhi 7 | * @date 2018/6/24 8 | */ 9 | public class Coder { 10 | 11 | private boolean hasGirlFriend; 12 | 13 | public Coder(boolean hasGirlFriend) { 14 | this.hasGirlFriend = hasGirlFriend; 15 | } 16 | 17 | public boolean hasGirlFriend() { 18 | return this.hasGirlFriend; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/chapter_06/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_06; 2 | 3 | /** 4 | * 为代码中的瑕疵写注释 5 | * 6 | * @author biezhi 7 | * @date 2018/7/6 8 | */ 9 | public class Example4 { 10 | 11 | /** 12 | * TODO 优化算法的性能 13 | */ 14 | public void foo() { 15 | 16 | } 17 | 18 | public void bar(String suffix) { 19 | if ("jpg".equals(suffix)) { 20 | System.out.println(suffix); 21 | } else { 22 | // TODO 处理 jpg 之外的其他格式 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/chapter_11/Example3.js: -------------------------------------------------------------------------------- 1 | $.post({ 2 | url: 'http://example.com/submit', 3 | data: data, 4 | success: function (response_data) { 5 | alert(format_pretty(response_data)); 6 | // 继续处理响应数据 7 | } 8 | }); 9 | 10 | function format_pretty(json_object) { 11 | var str = '{\n'; 12 | for (var key in json_object) { 13 | str += " " + key + " = " + response_data[key] + "\n"; 14 | } 15 | return str + "}"; 16 | } -------------------------------------------------------------------------------- /src/main/java/chapter_03/BinaryTree.java: -------------------------------------------------------------------------------- 1 | package chapter_03; 2 | 3 | /** 4 | * 二叉树 5 | * 6 | * @author biezhi 7 | * @date 2018/6/24 8 | */ 9 | public class BinaryTree { 10 | 11 | public int size() { 12 | return -1; 13 | } 14 | 15 | public int height(){ 16 | return -1; 17 | } 18 | 19 | public int nodeNumbers(){ 20 | return -1; 21 | } 22 | 23 | public int memoryBytes(){ 24 | return -1; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/chapter_10/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_10; 2 | 3 | /** 4 | * 缩小变量的作用域 5 | * 6 | * @author biezhi 7 | * @date 2018/7/25 8 | */ 9 | public class Example3 { 10 | 11 | public void method1() { 12 | String username = "abc"; 13 | // String.format() 14 | System.out.println(method2(username)); 15 | } 16 | 17 | public boolean method2(String username) { 18 | return null != username && username.length() > 6; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/chapter_04/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_04; 2 | 3 | /** 4 | * 相亲 5 | * 6 | * @author biezhi 7 | * @date 2018/6/27 8 | */ 9 | public class Example2 { 10 | 11 | public static final int MIN_DEPOSIT = 100_000; 12 | 13 | private int searchDeposit(String userName) { 14 | return 2000; 15 | } 16 | 17 | public void invoke() { 18 | if (searchDeposit("os7blue") < MIN_DEPOSIT) { 19 | System.out.println("您预约的妹子已下线"); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/chapter_07/Example9.java: -------------------------------------------------------------------------------- 1 | package chapter_07; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Paths; 5 | 6 | /** 7 | * 精确的描述方法的行为 8 | * 9 | * @author biezhi 10 | * @date 2018/7/7 11 | */ 12 | public class Example9 { 13 | 14 | /** 15 | * 返回此文件中有多少换行("\n") 16 | */ 17 | int countLines(String fileName) { 18 | try { 19 | return Files.readAllLines(Paths.get(fileName)).size(); 20 | } catch (Exception e) { 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/chapter_06/Example6.java: -------------------------------------------------------------------------------- 1 | package chapter_06; 2 | 3 | /** 4 | * 公布可能的陷阱 5 | * 6 | * @author biezhi 7 | * @date 2018/7/6 8 | */ 9 | public class Example6 { 10 | 11 | // 调用外部API发送邮件,1分钟后超时。 12 | void sendEmail(String to, String subject, String body) { 13 | 14 | } 15 | 16 | // 运行时间可以达到 O(标签数 * 标签深度),小心深层嵌套的输入 17 | void fixBrokenHtml(String html) { 18 | 19 | } 20 | 21 | /** 22 | * 这个文件包含一些辅助方法,为我们的文件系统提供了方便的接口。 23 | * 它处理了文件权限和一些其他的细节。 24 | */ 25 | class BiezhiUtils { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/chapter_08/README.md: -------------------------------------------------------------------------------- 1 | # 简化流程让代码易读 2 | 3 | | 编程结构 | 高层次程序流程是人如何变得不清晰的 | 4 | |-------|:--------:| 5 | | 线程 | 不清楚什么时候会执行代码 | 6 | | 信号量/中断处理程序 | 有些代码随时都有可能执行 | 7 | | 异常 | 可能会从多个函数向上冒泡的执行 | 8 | | 匿名函数 | 很难知道到底会执行什么代码,因为在编译器还未确定 | 9 | 10 | # 总结 11 | 12 | 1. 写一个比较时,把改变的值放在左边,把稳定的值放在右边 13 | 2. 可以重新排列 if else 代码块,优先处理正确的、简单的逻辑。 14 | 有时这些准则会冲突,当不冲突时,遵循这些经验法则。 15 | 3. 像三目运算符、do while循环经常会导致代码可读性变差。最好不要使用它们, 16 | 因为总是有更整洁的方式。 17 | 4. 嵌套的代码块需要花一些时间去理解。每层新的嵌套都会给读者“思维栈” push 18 | 一条数据。应该让它们变得“线性”,来避免深层嵌套。 19 | 5. 提早返回可以让代码更整洁。 -------------------------------------------------------------------------------- /src/main/java/chapter_02/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_02; 2 | 3 | /** 4 | * @author biezhi 5 | * @date 2018/6/24 6 | */ 7 | public class Example2 { 8 | 9 | private int cap = 2333; 10 | private double weight = 1.5; 11 | 12 | public double before() { 13 | return cap >= 0 ? weight * (1 << cap) : weight / (1 << -cap); 14 | } 15 | 16 | public double after() { 17 | if (cap >= 0){ 18 | return weight * (1 << cap); 19 | } 20 | return weight / (1 << -cap); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/chapter_06/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_06; 2 | 3 | /** 4 | * 记录你的思想 5 | * 6 | * @author biezhi 7 | * @date 2018/7/6 8 | */ 9 | public class Example3 { 10 | 11 | /** 12 | * 出乎意料的是,对于这些数据用二叉树比 Map 快 40% 13 | *

14 | * 哈希运算的代价比左/右大得多 15 | */ 16 | public void foo() { 17 | 18 | } 19 | 20 | /** 21 | * 作为整体可能会丢掉几个词。 22 | *

23 | * 这没问题,要 100% 解决太难了 24 | */ 25 | public void bar() { 26 | 27 | } 28 | 29 | /** 30 | * 这个方法变得越来越乱 31 | *

32 | * 也许可以创建一个 foo 来帮助分解 33 | */ 34 | public void boobar() { 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/chapter_10/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_10; 2 | 3 | /** 4 | * 减少控制流变量 5 | * 6 | * @author biezhi 7 | * @date 2018/7/25 8 | */ 9 | public class Example2 { 10 | 11 | int foo = 10; 12 | int bar = 20; 13 | 14 | public void part1() { 15 | while (foo < bar) { 16 | if(!isDone()){ 17 | break; 18 | } 19 | foo++; 20 | if (foo > bar) { 21 | continue; 22 | } 23 | } 24 | } 25 | 26 | private boolean isDone(){ 27 | return false; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/chapter_07/Example12.java: -------------------------------------------------------------------------------- 1 | package chapter_07; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.LocalDate; 5 | 6 | /** 7 | * 采用信息量高的词 8 | * 9 | * @author biezhi 10 | * @date 2018/7/7 11 | */ 12 | public class Example12 { 13 | 14 | /** 15 | * 该类充当数据库的缓存层 16 | */ 17 | class StoreAccounts { 18 | 19 | String username; 20 | String roleName; 21 | Integer score; 22 | LocalDate birthday; 23 | BigDecimal amount; 24 | // ... 25 | } 26 | 27 | /** 28 | * 规范化街道地址(删除多余的空格,简化词组。如将“Avenue”变为“Ave”) 29 | */ 30 | public void cleanStreet() { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/chapter_09/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_09; 2 | 3 | /** 4 | * 用作解释的变量 5 | * 6 | * @author biezhi 7 | * @date 2018/7/14 8 | */ 9 | public class Example1 { 10 | 11 | public void part1() { 12 | String line = "hello : world"; 13 | if ("hello".equals(line.split(":")[0].trim())) { 14 | // TODO 15 | } 16 | } 17 | 18 | public void part2() { 19 | String line = "hello : world"; 20 | String hello = line.split(":")[0].trim(); 21 | if ("hello".equals(hello)) { 22 | // TODO 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/chapter_03/Example5.java: -------------------------------------------------------------------------------- 1 | package chapter_03; 2 | 3 | /** 4 | * 用具体的名字代替抽象的名字 5 | * 6 | * @author biezhi 7 | * @date 2018/6/24 8 | */ 9 | public class Example5 { 10 | 11 | /** 12 | * VM options -DrunLocally=true 13 | */ 14 | public void start() { 15 | String runLocally = System.getProperty("runLocally", "false"); 16 | System.out.println("runLocally: " + runLocally); 17 | 18 | String extraLogging = System.getProperty("extraLogging", "false"); 19 | } 20 | 21 | public static void main(String[] args) { 22 | new Example5().start(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/chapter_06/README.md: -------------------------------------------------------------------------------- 1 | # 什么样的注释是好的 2 | 3 | | 标记 | 含义 | 4 | |-------|:--------:| 5 | | TODO | 等待处理的事情 | 6 | | FIXME | 已知无法运行的代码 | 7 | | HACK | 对一个问题不得不采用的简单粗暴的方案 | 8 | | XXX | 危险,这里有严重的问题 | 9 | 10 | ## 该写什么样的注释 11 | 12 | 注释的目的是帮助读代码的人了解作者在写代码时的思想。 13 | 14 | ## 什么地方不需要注释: 15 | 16 | - 能从代码本身中快速推断的事实 17 | - 用来装饰垃圾代码(比如拗口的方法名),实际上应该把名称修改好 18 | 19 | ## 记录你的想法 20 | 21 | - 为什么代码写成这样而不是另一个样子的内在理由(“指导性批注”) 22 | - 代码中的不足,使用像 TODO 或者 XXX 这样的标记 23 | - 常量背后的意义,为什么是这个值? 24 | 25 | ## 在读者的立场思考 26 | 27 | - 预料到代码中哪些部分会让读者说:“哎嘿?什么鬼” 给它们加上注释 28 | - 为小白意料之外的行为加注释 29 | - 在文件、类级别上使用“全局观”注释来解释所有的部分是如何一起工作的 30 | - 用注释总结代码块,让读者不会迷茫在细节里 31 | -------------------------------------------------------------------------------- /src/main/java/chapter_14/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_14; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * 删除无用代码 7 | * 8 | * @author biezhi 9 | * @date 2018/9/24 10 | */ 11 | public class Example3 { 12 | 13 | public Map readData(){ 14 | Map result = new HashMap<>(); 15 | result.put("title", i18n("title", "cn")); 16 | result.put("bannerText", i18n("bannerText", "cn")); 17 | result.put("copyright", i18n("copyright", "cn")); 18 | 19 | return result; 20 | } 21 | 22 | public String i18n(String key, String language){ 23 | return ""; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/chapter_04/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_04; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 范围 8 | * 9 | * @author biezhi 10 | * @date 2018/6/27 11 | */ 12 | public class Example3 { 13 | 14 | private List ageRange(int minAge, int maxAge) { 15 | List list = new ArrayList<>(maxAge - minAge); 16 | for (int i = minAge; i < maxAge; i++) { 17 | list.add(i); 18 | } 19 | return list; 20 | } 21 | 22 | public void invoke() { 23 | List range = ageRange(20, 35); 24 | System.out.println(range); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/chapter_05/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_05; 2 | 3 | /** 4 | * 把声明按块组织起来 5 | * 6 | * @author biezhi 7 | * @date 2018/7/3 8 | */ 9 | public class Example4 { 10 | 11 | interface RouteContext { 12 | 13 | // 读取Request信息 14 | String uri(); 15 | String contentType(); 16 | String header(String name); 17 | String cookie(String name); 18 | String pathString(String name); 19 | 20 | // 向Request写入数据 21 | RouteContext attribute(String key, Object value); 22 | 23 | // 向客户端发送数据 24 | void render(String view); 25 | RouteContext status(int status); 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/chapter_03/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_03; 2 | 3 | /** 4 | * 选择专业的词 5 | *

6 | * 找到更有表现力的词 7 | * 8 | * @author biezhi 9 | * @date 2018/6/24 10 | */ 11 | public class Example1 { 12 | 13 | // downloadPage fetchPage 14 | public String getPage(String url) { 15 | // 抓取URL页面信息返回 16 | return ""; 17 | } 18 | 19 | // 播放器 20 | static class Player { 21 | 22 | // 暂停播放 23 | public void pause(){ 24 | 25 | } 26 | 27 | // 恢复播放 28 | public void resume(){ 29 | 30 | } 31 | 32 | @Deprecated 33 | public void stop() { 34 | 35 | } 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/chapter_07/Example11.java: -------------------------------------------------------------------------------- 1 | package chapter_07; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 声明代码的意图 7 | * 8 | * @author biezhi 9 | * @date 2018/7/7 10 | */ 11 | public class Example11 { 12 | static class Product{ 13 | Integer price; 14 | } 15 | public void displayProducts(List products){ 16 | products.sort(this::compareProductByPrice); 17 | 18 | // 从高到低的显示每个商品的价格 19 | for (Product product: products) { 20 | displayPrice(product.price); 21 | } 22 | } 23 | 24 | private void displayPrice(Integer price) { 25 | } 26 | 27 | private int compareProductByPrice(Product p1, Product p2) { 28 | return p2.price.compareTo(p1.price); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/chapter_10/Example4.js: -------------------------------------------------------------------------------- 1 | // var submmitted = false; // 全局变量 2 | 3 | // var submit_form = function (form_name) { 4 | // if(submmitted) { 5 | // return; // 不能重复提交 6 | // } 7 | // // 向后端发送请求 8 | // // ... 9 | // submmitted = true; 10 | // }; 11 | 12 | var submit_form = (function () { 13 | var submmitted = false; // 局部变量,只在闭包内生效 14 | return function (form_name) { 15 | if(submmitted){ 16 | return; 17 | } 18 | submmitted = true; 19 | } 20 | }()); 21 | 22 | 23 | var f = function () { 24 | // 定义 i 的时候没有使用 var 声明 25 | for (var i = 0; i < 10; i++){ 26 | // 27 | } 28 | }; 29 | f(); 30 | 31 | console.log(i); -------------------------------------------------------------------------------- /src/main/java/chapter_06/Example5.java: -------------------------------------------------------------------------------- 1 | package chapter_06; 2 | 3 | import java.util.Vector; 4 | 5 | /** 6 | * 给常量加注释 7 | * 8 | * @author biezhi 9 | * @date 2018/7/6 10 | */ 11 | public class Example5 { 12 | 13 | // 只要它 >= 2 * CPU Core 就可以了 14 | private static final int NUM_THREADS = 8; 15 | 16 | // 添加一个限制,没有人可以订阅这么多 17 | private static final int MAX_RSS_SUBSCRIPTIONS = 1000; 18 | 19 | // 0.72 是经过计算的一个高质量参数 20 | private static final double IMAGE_QUALITY = 0.72D; 21 | 22 | static class Recorder { 23 | 24 | Vector data; 25 | 26 | void clear() { 27 | swap(data); // ??? 不是执行 data.clear() 么,什么鬼。。。 28 | } 29 | 30 | private void swap(Vector data) { 31 | 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/chapter_07/Example8.java: -------------------------------------------------------------------------------- 1 | package chapter_07; 2 | 3 | import java.util.HashMap; 4 | import javafx.util.Pair; 5 | import jdk.nashorn.internal.ir.RuntimeNode.Request; 6 | 7 | /** 8 | * 写出言简意赅的注释 9 | * 10 | * @author biezhi 11 | * @date 2018/7/6 12 | */ 13 | public class Example8 { 14 | 15 | // 这里的 Integer 是分类类型 16 | // Pair 内部的第一个浮点数是得分 17 | // 第二个浮点数是权重 18 | 19 | // CategoryType -> (score, weight) 20 | HashMap> scoreMap; 21 | 22 | // 从当前线程 Request 中获取查询参数 23 | public void queryParam(Request request) { 24 | 25 | } 26 | 27 | // 根据我们是否已经爬过这个URL,给它一个不同的优先级。 28 | public void crawledURL1() { 29 | 30 | } 31 | 32 | // 优先考虑之前从未爬过的网址 33 | public void crawledURL2() { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/chapter_03/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_03; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /** 7 | * 用前缀或后缀表达含义 8 | * 9 | * @author biezhi 10 | * @date 2018/6/24 11 | */ 12 | public class Example4 { 13 | 14 | private static final int DEFAULT_PORT = 9000; 15 | private static final List DEFAULT_STATICS = Arrays.asList("/static", "/upload"); 16 | 17 | // bad 18 | private String id = "af84ef845cd8"; 19 | private String hexId = "af84ef845cd8"; 20 | 21 | public void calcTime() { 22 | long startMs = System.currentTimeMillis(); 23 | // do something 24 | long elapsed = System.currentTimeMillis() - startMs; 25 | System.out.println("耗时: " + elapsed + "ms"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/chapter_11/Example5.js: -------------------------------------------------------------------------------- 1 | var max_results = get_cookie("max_results"); 2 | // name1=value1; name2=value2; 3 | // var cookies = document.cookie.split(';'); 4 | // for (var i = 0; i < cookies.length; i++) { 5 | // var c = cookies[i]; 6 | // c = c.replace(/^[ ]+/, ''); // 移除多余的空格 7 | // if (c.indexOf('max_results') === 0) { 8 | // max_results = Number(c.substring(12, c.length)); 9 | // } 10 | // } 11 | 12 | function get_cookie(key) { 13 | var cookies = document.cookie.split(';'); 14 | for (var i = 0; i < cookies.length; i++) { 15 | var c = cookies[i]; 16 | c = c.replace(/^[ ]+/, ''); // 移除多余的空格 17 | if (c.indexOf(key) === 0) { 18 | return c.substring(key.length + 1, c.length); 19 | } 20 | } 21 | return null; 22 | } -------------------------------------------------------------------------------- /src/main/java/chapter_06/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_06; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * 什么不需要注释 7 | * 8 | * @author biezhi 9 | * @date 2018/7/6 10 | */ 11 | public class Example1 { 12 | 13 | // 定义了一个账户类 14 | static class Account { 15 | 16 | // 构造器 17 | public Account() { 18 | } 19 | 20 | // 将利润设置为一个新值 21 | void setPorfit(double profit) { 22 | 23 | } 24 | 25 | // 返回账户的利润 26 | double getProfit() { 27 | return 2.9D; 28 | } 29 | 30 | } 31 | 32 | public void foo() { 33 | String line = "hello:world:2018"; 34 | 35 | // 删除第二个 ":" 之后的元素 36 | String[] newLines = Arrays.copyOfRange(line.split(":"), 0, 2); 37 | String name = String.join(":", newLines); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/chapter_08/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_08; 2 | 3 | /** 4 | * 三目运算符 5 | * 6 | * @author biezhi 7 | * @date 2018/7/11 8 | */ 9 | public class Example3 { 10 | 11 | int hour; 12 | 13 | public void part1() { 14 | String timeString = (hour >= 12) ? "pm" : "am"; 15 | 16 | if(hour >= 12){ 17 | timeString = "pm"; 18 | } else { 19 | timeString = "am"; 20 | } 21 | } 22 | 23 | public int part2() { 24 | int exponent = 20; // 计算出来的值 25 | int mantissa = -1; // 为了演示写死了 26 | if(exponent >= 0){ 27 | return mantissa * (1 << exponent); 28 | } 29 | return mantissa / (1 << exponent); 30 | // return exponent >= 0 ? mantissa * (1 << exponent) : 31 | // mantissa / (1 << exponent); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/chapter_03/Example6.java: -------------------------------------------------------------------------------- 1 | package chapter_03; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 名字该多长 8 | * 9 | * @author biezhi 10 | * @date 2018/6/24 11 | */ 12 | public class Example6 { 13 | 14 | private String newNavigationControllerWrappingViewControllerForDataSourceOfClass; 15 | 16 | private boolean debug; 17 | 18 | public void foo() { 19 | if (debug) { 20 | Map m = new HashMap<>(); 21 | lookUpNamesNumbers(m); 22 | System.out.println(m); 23 | } 24 | } 25 | 26 | private void lookUpNamesNumbers(Map m) { 27 | 28 | } 29 | 30 | private final long cMaxFileSize = 1024 * 10L; 31 | private final long MAX_FILE_SIZE = 1024 * 10L; 32 | 33 | public void usePattern() { 34 | int offset_ = 10; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/chapter_08/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_08; 2 | 3 | /** 4 | * 条件语句中参数的顺序 5 | * 6 | * @author biezhi 7 | * @date 2018/7/11 8 | */ 9 | public class Example1 { 10 | 11 | int length = 2018; 12 | long bytesReceived; 13 | long bytesExpected; 14 | 15 | public void part1() { 16 | if (length >= 2000) { 17 | System.out.println("你是一个现代程序员"); 18 | } 19 | } 20 | 21 | public void part2() { 22 | if (2000 <= length) { 23 | System.out.println("你是一个现代程序员??"); 24 | } 25 | } 26 | 27 | public void part3() { 28 | while (bytesReceived < bytesExpected) { 29 | System.out.println("请继续接收"); 30 | } 31 | } 32 | 33 | public void part4() { 34 | while (bytesExpected > bytesReceived) { 35 | System.out.println("请继续接收"); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/chapter_02/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_02; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /** 7 | * @author biezhi 8 | * @date 2018/6/24 9 | */ 10 | public class Example1 { 11 | 12 | Queue nodes = new LinkedList(); 13 | 14 | public Example1() { 15 | nodes.add(new Node()); 16 | nodes.add(new Node()); 17 | } 18 | 19 | public void snippet1() { 20 | for (Node node = nodes.peek(); node != null; node = node.next()) { 21 | System.out.println(node.data()); 22 | } 23 | } 24 | 25 | public void snippet2() { 26 | Node node = nodes.peek(); 27 | if (node == null) return; 28 | while (node.next() != null) { 29 | System.out.println(node.data()); 30 | node = node.next(); 31 | } 32 | if (node != null) System.out.println(node.data()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/chapter_08/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_08; 2 | 3 | /** 4 | * 避免用 do while 循环 5 | * 6 | * @author biezhi 7 | * @date 2018/7/11 8 | */ 9 | public class Example4 { 10 | 11 | int hour; 12 | 13 | class Node { 14 | String name; 15 | Node next; 16 | } 17 | 18 | // public boolean listHasNode(Node node, String name, int maxLength) { 19 | // do { 20 | // if (node.name.equals(name)) 21 | // return true; 22 | // node = node.next; 23 | // } while (node != null && --maxLength > 0); 24 | // return false; 25 | // } 26 | 27 | public boolean listHasNode(Node node, String name, int maxLength) { 28 | while (node != null && maxLength-- > 0){ 29 | if (node.name.equals(name)) 30 | return true; 31 | node = node.next; 32 | } 33 | return false; 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/chapter_09/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_09; 2 | 3 | /** 4 | * 与复杂的逻辑斗争 5 | * 6 | * @author biezhi 7 | * @date 2018/7/14 8 | */ 9 | public class Example4 { 10 | 11 | class Range{ 12 | int begin; 13 | int end; 14 | 15 | // 如:(0, 5) 会和 (3, 8) 重合 16 | // (0, 2) (2,4) 17 | boolean overlapsWith(Range other){ 18 | // 检查自己的任意端点是否在 other 的范围之内 19 | // return (begin >= other.begin && begin <= other.end) || 20 | // (end >= other.begin && end <= other.end); 21 | // return (begin >= other.begin && begin < other.end) || 22 | // (end > other.begin && end <= other.end) || 23 | // (begin <= other.begin && end >= other.end); 24 | if(other.end <= begin) return false; 25 | if(other.begin >= end) return false; 26 | return true; 27 | } 28 | } 29 | 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/chapter_12/Example1.js: -------------------------------------------------------------------------------- 1 | var vote_change = function (old_vote, new_vote) { 2 | var weight = get_weight(); 3 | // if (new_vote !== old_vote) { 4 | // if (new_vote === 'Up') { 5 | // weight += (old_vote === 'Up' ? 2 : 1); 6 | // } else if (new_vote === 'Down') { 7 | // weight -= (old_vote === 'Up' ? 2 : 1); 8 | // } else if (new_vote === '') { 9 | // weight += (old_vote === 'Up' ? -1 : 1); 10 | // } 11 | // set_weight(weight); 12 | // } 13 | weight -= get_vote_value(old_vote); 14 | weight += get_vote_value(new_vote); 15 | 16 | set_weight(weight); 17 | }; 18 | 19 | function get_vote_value(vote) { 20 | if(vote === 'Up'){ 21 | return +1; 22 | } 23 | if(vote === 'Down'){ 24 | return -1; 25 | } 26 | return 0; 27 | } 28 | 29 | function get_weight() { 30 | } 31 | function set_weight(weight) { 32 | } -------------------------------------------------------------------------------- /src/main/java/chapter_09/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_09; 2 | 3 | /** 4 | * 使用德摩根定理 5 | * 滥用短路逻辑 6 | * 7 | * @author biezhi 8 | * @date 2018/7/14 9 | */ 10 | public class Example3 { 11 | 12 | /** 13 | * 1) not (a or b or c) <=> (not a) and (not b) and (not c) 14 | * 2) not (a and b and c) <=> (not a) or (not b) or (not c) 15 | */ 16 | public void part1(boolean fileExists, boolean isProtected) { 17 | // if (!(fileExists && !isProtected)) { 18 | // throw new RuntimeException("对不起,无法读取该文件"); 19 | // } 20 | if (!fileExists || isProtected) { 21 | throw new RuntimeException("对不起,无法读取该文件"); 22 | } 23 | } 24 | 25 | 26 | public void part2() { 27 | // assert((!(bucket = findBucket(key)) && bucket->isOccupied())) 28 | // bucket = findBucket(key) 29 | // if bucket != null { 30 | // assert bucket.isOccupied 31 | // } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/chapter_13/Example2.js: -------------------------------------------------------------------------------- 1 | //

尝试搜索:王爵的技术小黑屋
2 | //
尝试搜索:流行编程字体
3 | //
尝试搜索:Github 使用技巧
4 | 5 | // 6 | 7 | // var show_next_tip = function(){ 8 | // var num_tips = $('.tip').size(); 9 | // var show_tip = $('.tip:visible'); 10 | // 11 | // var show_tip_num = Number(show_tip.attr('id').slice(4)); 12 | // if(show_tip_num === num_tips) { 13 | // $('#tip-1').show(); 14 | // } else { 15 | // $('#tip-' + (show_tip_num + 1)).show(); 16 | // } 17 | // show_tip.hide(); 18 | // }; 19 | 20 | var show_next_tip = function(){ 21 | // 隐藏当前显示的搜索记录 22 | var cur_tip = $('.tip:visited').hide(); 23 | // 找到下一个搜索记录 24 | var next_tip = cur_tip.next('.tip'); 25 | if(next_tip.size() === 0){ 26 | next_tip = $('.tip:first'); 27 | } 28 | next_tip.show(); 29 | }; -------------------------------------------------------------------------------- /src/main/java/chapter_06/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_06; 2 | 3 | /** 4 | * 不用为了注释而注释 5 | * 不要给不好的名字加注释——应该把名字改好 6 | * 7 | * @author biezhi 8 | * @date 2018/7/6 9 | */ 10 | public class Example2 { 11 | 12 | static class Node { 13 | 14 | String name; 15 | } 16 | 17 | /** 18 | * 查找具有给定名称的节点或者返回一个null 19 | * 20 | * 1. 如果深度 <= 0, 只检查子树 21 | * 2. 如果深度 == N, 只检查子树和 N 级以下 22 | */ 23 | Node findNodeInSubTree(Node subTree, String name, int depth) { 24 | return null; 25 | } 26 | 27 | /** 28 | * 对请求中的回复状态做限制 29 | *

30 | * 例如返回的项目数或总字节大小等。 31 | */ 32 | void cleanReply(String request, String reply) { 33 | 34 | } 35 | 36 | /** 37 | * 对请求做一个限制,确保回复状态符合 “次数/字节” 等等 38 | * @param request 39 | * @param reply 40 | */ 41 | void enforceLimitsForRequest(String request, String reply){ 42 | 43 | } 44 | 45 | /** 46 | * 释放本地的 registryKey,不会修改注册中心的实际数据 47 | */ 48 | void releaseRegistry(String registryKey) { 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/chapter_10/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_10; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.function.BiConsumer; 7 | 8 | /** 9 | * 减少变量 10 | * 11 | * @author biezhi 12 | * @date 2018/7/21 13 | */ 14 | public class Example1 { 15 | 16 | class Message { 17 | LocalDateTime lastViewTime; 18 | } 19 | 20 | public void part1(Message rootMessage) { 21 | rootMessage.lastViewTime = LocalDateTime.now(); 22 | } 23 | 24 | 25 | public void part2() { 26 | BiConsumer, Integer> consumer = (array, valueToRemove) -> { 27 | for (int i = 0; i < array.size(); i++) { 28 | if (array.get(i).equals(valueToRemove)) { 29 | array.remove(i); 30 | break; 31 | } 32 | } 33 | }; 34 | consumer.accept(Arrays.asList(1, 2, 3), 2); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/chapter_09/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_09; 2 | 3 | /** 4 | * 总结变量 5 | * 6 | * @author biezhi 7 | * @date 2018/7/14 8 | */ 9 | public class Example2 { 10 | 11 | class User { 12 | 13 | long id; 14 | } 15 | 16 | class Request { 17 | 18 | User user; 19 | } 20 | 21 | class Document { 22 | 23 | long ownerId; 24 | } 25 | 26 | public void part1(Request request, Document document) { 27 | if (request.user.id == document.ownerId) { 28 | // 允许该用户编辑文档 29 | } 30 | if (request.user.id != document.ownerId) { 31 | // 文档只读 32 | } 33 | } 34 | 35 | public void part2(Request request, Document document) { 36 | boolean userOwnsDocument = (request.user.id == document.ownerId); 37 | if (userOwnsDocument) { 38 | // 允许该用户编辑文档 39 | } 40 | if (!userOwnsDocument) { 41 | // 文档只读 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/chapter_09/Example6.java: -------------------------------------------------------------------------------- 1 | package chapter_09; 2 | 3 | import org.apache.commons.beanutils.BeanUtils; 4 | 5 | /** 6 | * 简化表达式的创意方法 7 | * 8 | * @author biezhi 9 | * @date 2018/7/14 10 | */ 11 | public class Example6 { 12 | 13 | class Stats { 14 | 15 | long totalMemory; 16 | long freeMemory; 17 | long swapMemory; 18 | int numProcesses; 19 | String statusString; 20 | } 21 | 22 | public void addStats(Stats addFrom, Stats addTo) { 23 | addTo.totalMemory = addFrom.totalMemory + addTo.totalMemory; 24 | addTo.freeMemory = addFrom.freeMemory + addTo.freeMemory; 25 | addTo.swapMemory = addFrom.swapMemory + addTo.swapMemory; 26 | addTo.swapMemory = addFrom.swapMemory + addTo.swapMemory; 27 | addTo.statusString = addFrom.statusString + addTo.statusString; 28 | addTo.numProcesses = addFrom.numProcesses + addTo.numProcesses; 29 | // ... 30 | // BeanUtils.copyProperties(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/chapter_11/Example1.js: -------------------------------------------------------------------------------- 1 | // 返回 'array' 中哪个位置最接近给定的经纬度 2 | // 将地球塑造成一个完美的球体。 3 | var findClosestLocation = function (lat, lng, array) { 4 | var closest; 5 | var closest_dist = Number.MAX_VALUE; 6 | for (var i = 0; i < array.length; i++) { 7 | 8 | var dist = spherical_distance(lat, lng, array[i].latitude, array[i].longitude); 9 | if (dist < closest_dist) { 10 | closest = array[i]; 11 | closest_dist = dist; 12 | } 13 | } 14 | return closest; 15 | }; 16 | 17 | function spherical_distance(lat, lng, latitude, longitude) { 18 | // 将两个点转换为弧度 19 | var lat_rad = radians(lat); 20 | var lng_rad = radians(lng); 21 | var lat2_rad = radians(latitude); 22 | var lng2_rad = radians(longitude); 23 | 24 | // 使用'球面三角形的余弦定理'公式 25 | return Math.acos(Math.sin(lat_rad) * Math.sin(lat2_rad) + 26 | Math.cos(lat_rad) * Math.cos(lat2_rad) + 27 | Math.cos(lng2_rad - lng_rad)); 28 | } 29 | 30 | function radians(point) { 31 | return 1; 32 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 编写可读代码的艺术 2 | 3 | 编写高质量的代码是程序员追求的目标,这个视频以《编写可读代码的艺术》为蓝本, 4 | 使用 Java 语言讲解其中的优化手段。 5 | 6 | ## 课程地址 7 | 8 | - [Youtube 频道](https://www.youtube.com/c/biezhi) 9 | - [Bilibili](https://space.bilibili.com/33165125) 10 | 11 | ## 章节 12 | 13 | - [01. 课程介绍](https://youtu.be/SNYwZ1l9blc) 14 | - [02. 写让人理解的代码](https://youtu.be/GYTgrSQO8fs) 15 | - [03. 把信息装到名字里](https://youtu.be/aEj9YF8uxts) 16 | - [04. 让人不会误解的名字](https://youtu.be/ssuSlE3gSxc) 17 | - [05. 写代码也需要审美?](https://youtu.be/X6OIaGhCt1s) 18 | - [06. 什么样的注释是好的?](https://youtu.be/C1g0xLfwD74) 19 | - [07. 写言简意赅的注释](https://youtu.be/yR2aXO0doRk) 20 | - [08. 简化流程让代码易读](https://youtu.be/v6A231LCdAQ) 21 | - [09. 拆分又臭又长的表达式](https://youtu.be/7_0ZhB0bJV0) 22 | - [10. 变量和可读性](https://youtu.be/K8oognOKIlA) 23 | - [11. 抽取无关的代码](https://youtu.be/no8M8C1HNuI) 24 | - [12. 一次只干一件事](https://youtu.be/atrR3_h-AhA) 25 | - [13. 把想法变成代码](https://youtu.be/b_CiksFfIvI) 26 | - [14. 少写点代码](https://youtu.be/yuRAvtmwgzY) 27 | - [15. 测试和可读性](#) - 待更新 28 | - [16. 你的最终考验](#) - 待更新 29 | 30 | ## License 31 | 32 | [BSD3](LICENSE) 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/chapter_05/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_05; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 为什么审美是重要的 7 | * 8 | * @author biezhi 9 | * @date 2018/7/3 10 | */ 11 | public class Example1 { 12 | 13 | static abstract class StatsKeeper1 { 14 | // A class for keeping track of a series of doubles 15 | abstract void add(double d); // and methods for quick statistics about them 16 | private int count; /* how many so far 17 | */ public abstract double avgerage(); 18 | private double minimum; 19 | private List pastItems 20 | ; double maximum; 21 | 22 | } 23 | 24 | // A class for keeping track of a series of doubles 25 | // and methods for quick statistics about them 26 | static abstract class StatsKeeper2 { 27 | 28 | abstract void add(double d); 29 | abstract double avgerage(); 30 | 31 | private List pastItems; 32 | private int count; // how many so far 33 | 34 | private double minimum; 35 | private double maximum; 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/chapter_14/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_14; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | import static java.util.stream.Collectors.toList; 10 | 11 | /** 12 | * 熟悉标准库 13 | * 14 | * @author biezhi 15 | * @date 2018/9/24 16 | */ 17 | public class Example4 { 18 | 19 | private List list = Arrays.asList(2, 3, 3, 3); 20 | 21 | // public List unique(List elements) { 22 | // List result = new ArrayList<>(); 23 | // for (Integer element : elements) { 24 | // if (!result.contains(element)) { 25 | // result.add(element); 26 | // } 27 | // } 28 | // return result; 29 | // } 30 | 31 | // public List unique(List elements) { 32 | // return new ArrayList<>(new HashSet<>(elements)); 33 | // } 34 | 35 | public List unique(List elements) { 36 | return elements.stream().distinct().collect(toList()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/chapter_11/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_11; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | 8 | /** 9 | * 纯工具代码 10 | * 11 | * @author biezhi 12 | * @date 2018/8/7 13 | */ 14 | public class Example2 { 15 | 16 | public void foo(String fileName) { 17 | try { 18 | System.out.println(readFileToString(fileName)); 19 | // TODO 20 | } catch (Exception e) { 21 | // Exception 22 | } 23 | } 24 | 25 | public String readFileToString(String filePath) throws IOException { 26 | StringBuilder currentLines = new StringBuilder(); 27 | File file = new File(filePath); 28 | FileReader fr = new FileReader(file); 29 | BufferedReader bufferedReader = new BufferedReader(fr); 30 | String sCurrentLine; 31 | while ((sCurrentLine = bufferedReader.readLine()) != null) { 32 | currentLines.append(sCurrentLine).append("\n"); 33 | } 34 | return currentLines.toString(); 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/chapter_02/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_02; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author biezhi 8 | * @date 2018/6/24 9 | */ 10 | public class Example3 { 11 | 12 | private Map coderMap = new HashMap(); 13 | 14 | public Example3() { 15 | coderMap.put("biezhi", new Coder(true)); 16 | coderMap.put("80k", new Coder(false)); 17 | } 18 | 19 | private Coder findCoder(String key) { 20 | return coderMap.get(key); 21 | } 22 | 23 | public void before() { 24 | Coder coder; 25 | boolean hasGirlFriend = (null != (coder = findCoder("biezhi"))) && coder.hasGirlFriend(); 26 | System.out.println("是否有女朋友: " + hasGirlFriend); 27 | } 28 | 29 | public void after() { 30 | Coder coder = this.findCoder("biezhi"); 31 | if(null != coder){ 32 | System.out.println("是否有女朋友: " + coder.hasGirlFriend()); 33 | } 34 | } 35 | 36 | public static void main(String[] args) { 37 | new Example3().before(); 38 | new Example3().after(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.biezhi 8 | write-readable-code 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | commons-beanutils 14 | commons-beanutils 15 | 1.9.3 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 25 | 1.8 26 | 1.8 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/chapter_14/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_14; 2 | 3 | /** 4 | * 缓存设计 5 | *

6 | * 读取 a.txt 7 | * 读取 a.txt 8 | * 读取 a.txt 9 | * 读取 b.txt 10 | * 读取 b.txt 11 | * 读取 c.txt 12 | * 读取 d.txt 13 | * 读取 d.txt 14 | * 15 | * @author biezhi 16 | * @date 2018/9/24 17 | */ 18 | public class Example2 { 19 | 20 | private DiskText lastUsed; 21 | 22 | // 加载磁盘文件 23 | public DiskText loadDiskText(String key) { 24 | // 从本地加载 25 | return new DiskText(key, ""); 26 | } 27 | 28 | // 根据 key 获取磁盘文件 29 | public DiskText lookUp(String key) { 30 | if (lastUsed == null || !lastUsed.key().equals(key)) { 31 | lastUsed = loadDiskText(key); 32 | } 33 | return lastUsed; 34 | } 35 | 36 | class DiskText { 37 | private String key; 38 | private String value; 39 | 40 | public DiskText(String key, String value) { 41 | this.key = key; 42 | this.value = value; 43 | } 44 | 45 | public String value() { 46 | return value; 47 | } 48 | 49 | public String key() { 50 | return key; 51 | } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/main/java/chapter_06/Example7.java: -------------------------------------------------------------------------------- 1 | package chapter_06; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * 总结性注释 10 | * 11 | * @author biezhi 12 | * @date 2018/7/6 13 | */ 14 | public class Example7 { 15 | 16 | private List customers = Arrays.asList(1L, 3L, 45L, 62L, 98L); 17 | private Map> allSales = new HashMap<>(); 18 | 19 | static class Sale { 20 | 21 | Long recipient; 22 | } 23 | 24 | public void foo() { 25 | // 找到客户购买的所有商品 26 | for (Long customerId: customers) { 27 | for (Sale sale: allSales.get(customerId)) { 28 | if (sale.recipient.equals(customerId)) { 29 | // do something 30 | } 31 | } 32 | } 33 | } 34 | 35 | public void generateUserReport() { 36 | // 获取当前用户的锁 37 | // .... 省略一部分代码 38 | 39 | // 从数据库中读取用户的信息 40 | // .... 省略一部分代码 41 | 42 | // 将信息写入到文件 43 | // .... 省略一部分代码 44 | 45 | // 释放当前用户的锁 46 | } 47 | 48 | // 1. 不管你心里在想什么,先把它记录下来 49 | // 2. 读一下这段注释,看看它有什么需要改进的 50 | // 3. 不断改进 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/chapter_09/Example5.js: -------------------------------------------------------------------------------- 1 | // var update_highlight = function (message_num) { 2 | // if ($("#vote_value" + message_num).html() === "Up") { 3 | // $("thumbs_up" + message_num).addClass('highlighted'); 4 | // $("thumbs_down" + message_num).removeClass('highlighted'); 5 | // } else if ($("#vote_value" + message_num).html() === "Down") { 6 | // $("thumbs_up" + message_num).removeClass('highlighted'); 7 | // $("thumbs_down" + message_num).addClass('highlighted'); 8 | // } else { 9 | // $("thumbs_up" + message_num).removeClass('highlighted'); 10 | // $("thumbs_down" + message_num).removeClass('highlighted'); 11 | // } 12 | // }; 13 | 14 | var update_highlight = function (message_num) { 15 | var thumbs_up = $("thumbs_up" + message_num); 16 | var thumbs_down = $("thumbs_down" + message_num); 17 | var vote_value = $("#vote_value" + message_num).html(); 18 | var hi = 'highlighted'; 19 | 20 | if (vote_value === "Up") { 21 | thumbs_up.addClass(hi); 22 | thumbs_down.removeClass(hi); 23 | } else if (vote_value === "Down") { 24 | thumbs_up.removeClass(hi); 25 | thumbs_down.addClass(hi); 26 | } else { 27 | thumbs_up.removeClass(hi); 28 | thumbs_down.removeClass(hi); 29 | } 30 | }; -------------------------------------------------------------------------------- /src/main/java/chapter_11/Example6.java: -------------------------------------------------------------------------------- 1 | package chapter_11; 2 | 3 | import java.util.Base64; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * 按需重构接口 9 | * 10 | * @author biezhi 11 | * @date 2018/8/13 12 | */ 13 | public class Example6 { 14 | 15 | class Cipher { 16 | String method; 17 | String key; 18 | 19 | public Cipher(String method, String key) { 20 | this.method = method; 21 | this.key = key; 22 | } 23 | 24 | public byte[] update(String str) { 25 | return str.getBytes(); 26 | } 27 | 28 | } 29 | 30 | private String toJson(Map map) { 31 | return null; 32 | } 33 | 34 | public void foo() { 35 | Map map = new HashMap<>(); 36 | map.put("username", "biezhi"); 37 | map.put("password", "123456"); 38 | 39 | String url = "http://exmaple.com/?user_info=" + urlSafeEncrypt(map); 40 | // ... 41 | } 42 | 43 | private String urlSafeEncrypt(Map map){ 44 | String json = toJson(map); 45 | Cipher cipher = new Cipher("aes_256", "pas5#w0rd"); 46 | byte[] encryptedBytes = cipher.update(json); 47 | return Base64.getEncoder().encodeToString(encryptedBytes); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/chapter_08/Example6.java: -------------------------------------------------------------------------------- 1 | package chapter_08; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /** 7 | * 减少循环嵌套 8 | * 9 | * @author biezhi 10 | * @date 2018/7/11 11 | */ 12 | public class Example6 { 13 | 14 | class User { 15 | String name; 16 | 17 | public User(String name) { 18 | this.name = name; 19 | } 20 | } 21 | 22 | // public void foo() { 23 | // List result = Arrays.asList(new User("jack"), 24 | // new User("biezhi")); 25 | // 26 | // int notNullCount = 0; 27 | // for (User user: result) { 28 | // if (user != null) { 29 | // notNullCount++; 30 | // if (user.name != null) { 31 | // System.out.println(user.name); 32 | // } 33 | // } 34 | // } 35 | // 36 | // } 37 | 38 | public void bar() { 39 | List result = Arrays.asList(new User("jack"), 40 | new User("biezhi")); 41 | 42 | int notNullCount = 0; 43 | for (User user : result) { 44 | if (null == user) { 45 | continue; 46 | } 47 | notNullCount++; 48 | if (user.name != null) { 49 | System.out.println(user.name); 50 | } 51 | } 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/chapter_03/README.md: -------------------------------------------------------------------------------- 1 | # 把信息装到名字里 2 | 3 | ## 有表现力的词 4 | 5 | | 单词 | 更多选择 | 6 | |-------|:--------:| 7 | | send | deliver、dispatch、announce、distribute、route | 8 | | find | search、extract、locate、recover | 9 | | start | launch、create、begin、open | 10 | | make | create、set up、build、generate、compose、add、new | 11 | 12 | ## 带单位的命名 13 | 14 | | 参数或变量 | 带单位的命名 | 15 | |-------|:--------:| 16 | | start(int delay) | delay -> delaySecs | 17 | | createCache(int size) | size -> sizeMB | 18 | | throttleDownload(float limit) | limit -> maxKB | 19 | | setHeight(float height) | height -> heightCM | 20 | 21 | ## 给名字附加额外信息 22 | 23 | | 场景 | 变量名 | 更好的名字 | 24 | |-------|:--------:|:-------:| 25 | | 一个纯文本的密码,需要加密后才可以使用 | password | plaintextPassword | 26 | | 一条用户评论,需要转义后显示 | comment | unescapedComment | 27 | | 已转化为UTF-8的HTML文本 | html | htmlUtf8 | 28 | | 以"URL"方式编码的输入数据 | data | dataURLEncode | 29 | 30 | **[谷歌代码规范](https://github.com/google/styleguide) | [中文](http://zh-google-styleguide.readthedocs.io/en/latest/)** 31 | 32 | - [Java 代码规范](https://google.github.io/styleguide/javaguide.html) 33 | - [C++ 代码规范](https://google.github.io/styleguide/cppguide.html) 34 | - [Python 代码规范](https://github.com/google/styleguide/blob/gh-pages/pyguide.md) 35 | - [JavaScript 代码规范](https://google.github.io/styleguide/jsguide.html) 36 | 37 | ## 总结 38 | 39 | 1. 使用专业的词 40 | 2. 避免使用空泛的词 41 | 3. 给变量名带上附加信息 42 | 4. 为作用域更大的变量起一个长的名字 43 | 5. 有目的的使用大小写和下划线 -------------------------------------------------------------------------------- /src/main/java/chapter_11/Example4.java: -------------------------------------------------------------------------------- 1 | package chapter_11; 2 | 3 | import java.time.LocalDate; 4 | 5 | /** 6 | * 项目专有的功能 7 | * 8 | * @author biezhi 9 | * @date 2018/8/13 10 | */ 11 | public class Example4 { 12 | 13 | class Request { 14 | String param(String key) { 15 | return ""; 16 | } 17 | } 18 | 19 | static class Business { 20 | String name; 21 | String url; 22 | LocalDate created; 23 | 24 | public void save() { 25 | } 26 | } 27 | 28 | public void part1(Request request) { 29 | Business business = new Business(); 30 | business.name = request.param("name"); 31 | 32 | business.url = "/biz/" + makePrettyURL(business); 33 | business.created = LocalDate.now(); 34 | business.save(); 35 | } 36 | 37 | private String makePrettyURL(Business business) { 38 | String urlPathName = business.name.toLowerCase(); 39 | urlPathName = urlPathName.replaceAll("[\\.,\\']", ""); 40 | urlPathName = urlPathName.replaceAll("[^a-z0-9]+", "-"); 41 | if (urlPathName.charAt(0) == '-') { 42 | urlPathName = urlPathName.substring(1); 43 | } 44 | if (urlPathName.charAt(urlPathName.length() - 1) == '-') { 45 | urlPathName = urlPathName.substring(0, urlPathName.length() - 1); 46 | } 47 | return urlPathName; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/chapter_05/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_05; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * 在需要时使用对齐 7 | * 8 | * @author biezhi 9 | * @date 2018/7/3 10 | */ 11 | public class Example3 { 12 | 13 | private Request request = new Request(); 14 | 15 | static class Request { 16 | Request GET; 17 | Request POST; 18 | private Map values; 19 | 20 | public String get(String key) { 21 | return values.get(key); 22 | } 23 | } 24 | 25 | public void part1() { 26 | // 从 POST 中获取数据 27 | String company = request.POST.get("company"); 28 | String kindergarten = request.POST.get("kindergarten"); 29 | String subwayStation = request.POST.get("subwayStation"); 30 | String zoo = request.POST.get("zoo"); 31 | 32 | } 33 | 34 | public void part2() { 35 | // 从 POST 中获取数据 36 | String company = request.POST.get("company"); 37 | String kindergarten = request.POST.get("kindergarten"); 38 | String subwayStation = request.POST.get("subwayStation"); 39 | String zoo = request.POST.get("zoo"); 40 | 41 | if (null != company) { /* company */} 42 | if (null != subwayStation) { /* subwayStation // 这不是去幼儿园的车?*/} 43 | if (null != zoo) { /* zoo */} 44 | if (null != kindergarten) { /* kindergarten // 为什么我在幼儿园? */} 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/main/java/chapter_08/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_08; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /** 7 | * if else 块的顺序 8 | * 9 | * @author biezhi 10 | * @date 2018/7/11 11 | */ 12 | public class Example2 { 13 | 14 | boolean a, b; 15 | 16 | public void part1() { 17 | // if (a == b) { 18 | // System.out.println("有钱人"); 19 | // } else { 20 | // System.out.println("没钱的肥宅"); 21 | // } 22 | if (a != b) { 23 | System.out.println("没钱的肥宅"); 24 | } else { 25 | System.out.println("有钱人"); 26 | } 27 | } 28 | 29 | class Request { 30 | boolean hasParam(String key) { 31 | return false; 32 | } 33 | } 34 | 35 | class Response { 36 | void writeJSON(Object bean) { 37 | 38 | } 39 | } 40 | 41 | public void part2(Request request, Response response) { 42 | 43 | List items = Arrays.asList("hello", "world"); 44 | 45 | // if (!request.hasParam("biezhi")) { 46 | // response.writeJSON(items); 47 | // } else { 48 | // for (String item: items) { 49 | // // TODO 50 | // } 51 | // } 52 | if (request.hasParam("biezhi")) { 53 | for (String item: items) { 54 | // TODO 55 | } 56 | } else { 57 | response.writeJSON(items); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/chapter_13/Example1.java: -------------------------------------------------------------------------------- 1 | package chapter_13; 2 | 3 | /** 4 | * 清楚的描述逻辑 5 | * 6 | * @author biezhi 7 | * @date 2018/9/1 8 | */ 9 | public class Example1 { 10 | 11 | private boolean isAdminRequest(){ 12 | return false; 13 | } 14 | 15 | private String noAuthorized(){ 16 | return "authorized.html"; 17 | } 18 | 19 | class Session { 20 | 21 | public String getAttribute(String key) { 22 | return ""; 23 | } 24 | } 25 | class Document { 26 | String username; 27 | } 28 | 29 | // public String part1(Document document, Session session){ 30 | // boolean isAdmin = isAdminRequest(); 31 | // if(null != document){ 32 | // if(!isAdmin && document.username != session.getAttribute("username")){ 33 | // return noAuthorized(); 34 | // } 35 | // } else { 36 | // if(!isAdmin) { 37 | // return noAuthorized(); 38 | // } 39 | // } 40 | // // 渲染正常页面 41 | // return "biezhi.html"; 42 | // } 43 | 44 | public String part2(Document document, Session session){ 45 | boolean isAdmin = isAdminRequest(); 46 | if(isAdmin){ 47 | // 获取信息 48 | } else if(document.username.equals(session.getAttribute("username"))){ 49 | // 获取信息 50 | } else { 51 | return noAuthorized(); 52 | } 53 | // 渲染正常页面 54 | return "biezhi.html"; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/chapter_03/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_03; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.util.function.Function; 6 | 7 | /** 8 | * 避免tmp和retval这样泛泛的名字 9 | * 10 | * @author biezhi 11 | * @date 2018/6/24 12 | */ 13 | public class Example2 { 14 | 15 | public void before() { 16 | // Function euclideanNorm = v -> { 17 | // double retval = 0; 18 | // for (int i = 0; i < v.length; i++) 19 | // retval += v[i] * v[i]; 20 | // return Math.sqrt(retval); 21 | // }; 22 | // 23 | // int[] numbs = {1, 2, 3, 5}; 24 | // Double apply = euclideanNorm.apply(numbs); 25 | // System.out.println(apply); 26 | } 27 | 28 | public void after() { 29 | Function euclideanNorm = v -> { 30 | double sumSquares = 0; 31 | for (int i = 0; i < v.length; i++) 32 | sumSquares += v[i] * v[i]; 33 | return Math.sqrt(sumSquares); 34 | }; 35 | 36 | int[] numbs = {1, 2, 3, 5}; 37 | Double apply = euclideanNorm.apply(numbs); 38 | System.out.println(apply); 39 | } 40 | 41 | public void numberSwap(int right, int left){ 42 | int tmp = right; 43 | right = left; 44 | left = tmp; 45 | } 46 | 47 | public void tempUser() throws IOException { 48 | // String tmp = user.getUserName(); 49 | 50 | Files.createTempFile("tmp_file", ".txt"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/chapter_10/Example7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 |

10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, 王爵nice 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/main/java/chapter_03/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_03; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 循环迭代器 8 | *

9 | * 用具体的名字代替抽象的名字 10 | * 11 | * @author biezhi 12 | * @date 2018/6/24 13 | */ 14 | public class Example3 { 15 | 16 | static class Club { 17 | 18 | public List members() { 19 | return new ArrayList<>(); 20 | } 21 | } 22 | 23 | public void before() { 24 | // List users = new ArrayList<>(); 25 | // List clubs = new ArrayList<>(); 26 | // 27 | // for (int i = 0; i < clubs.size(); i++) { 28 | // for (int j = 0; j < clubs.get(0).members().size(); j++) { 29 | // for (int k = 0; k < users.size(); k++) { 30 | // if (clubs.get(i).members().get(k) == users.get(j)) { 31 | // System.out.println("用户 [" + j + "] 在俱乐部 [" + i + "]"); 32 | // } 33 | // } 34 | // } 35 | // } 36 | 37 | } 38 | 39 | public void after() { 40 | List users = new ArrayList<>(); 41 | List clubs = new ArrayList<>(); 42 | 43 | for (int ci = 0; ci < clubs.size(); ci++) { 44 | for (int mi = 0; mi < clubs.get(0).members().size(); mi++) { 45 | for (int ui = 0; ui < users.size(); ui++) { 46 | if (clubs.get(ci).members().get(mi) == users.get(ui)) { 47 | System.out.println("用户 [" + mi + "] 在俱乐部 [" + ci + "]"); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/chapter_05/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_05; 2 | 3 | /** 4 | * 重新安排换行来保持一致和紧凑 5 | * 6 | * @author biezhi 7 | * @date 2018/7/3 8 | */ 9 | public class Example2 { 10 | 11 | static class Cookie { 12 | private String name; 13 | private String value; 14 | private int maxAge; 15 | private boolean isSecure; 16 | 17 | public Cookie(String name, String value, int maxAge, boolean isSecure) { 18 | this.name = name; 19 | this.value = value; 20 | this.maxAge = maxAge; 21 | this.isSecure = isSecure; 22 | } 23 | } 24 | 25 | public void part1(){ 26 | Cookie c1 = new Cookie( 27 | "uid",/* key */ 28 | "2018", /* value */ 29 | 1800,/* expire time, seconds */ 30 | false /* is safe */ 31 | ); 32 | 33 | Cookie c2 = new Cookie( 34 | "PICK_KEY",/* key */ 35 | "ahmd13ldsws8cw", /* value */10800,/* expire time, seconds */ 36 | true /* is safe */ 37 | ); 38 | 39 | Cookie c3 = new Cookie("REMEMBER_ME",/* key */ 40 | "true", /* value */10800,/* expire time, seconds */ 41 | true /* is safe */ 42 | ); 43 | } 44 | 45 | public void part2(){ 46 | // Cookie(name, value , maxAge, isSecure) 47 | // [string | string | seconds | true/false] 48 | Cookie c1 = new Cookie("uid", "2018", 1800,false); 49 | Cookie c2 = new Cookie("PICK_KEY", "ahmd13ldsws8cw",10800,true); 50 | Cookie c3 = new Cookie("REMEMBER_ME","true",10800,true); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/chapter_04/Example5.java: -------------------------------------------------------------------------------- 1 | package chapter_04; 2 | 3 | /** 4 | * list.size 5 | * 6 | * @author biezhi 7 | * @date 2018/6/27 8 | */ 9 | public class Example5 { 10 | 11 | static class List { 12 | private E[] data; 13 | private int pos; 14 | 15 | public List(E[] data) { 16 | this.data = data; 17 | } 18 | 19 | int countSize() { 20 | int size = 0; 21 | for (E e: data) { 22 | if (null != e) { 23 | size += 1; 24 | } 25 | } 26 | return size; 27 | } 28 | 29 | int size() { 30 | return data.length - pos; 31 | } 32 | 33 | E popBack() { 34 | pos++; 35 | E lastItem = data[data.length - pos]; 36 | data[data.length - pos] = null; 37 | return lastItem; 38 | } 39 | 40 | } 41 | 42 | private void shrinkList(List list, int maxSize) { 43 | while (list.size() > maxSize) { 44 | freeNode(list.popBack()); 45 | } 46 | } 47 | 48 | private void freeNode(String item) { 49 | System.out.println("释放了: " + item); 50 | } 51 | 52 | public static void main(String[] args) { 53 | String[] items = new String[10_0000]; 54 | 55 | for (int i = 0; i < items.length; i++) { 56 | items[i] = "hello_#" + i; 57 | } 58 | 59 | List list = new List<>(items); 60 | Example5 example5 = new Example5(); 61 | long startMs = System.currentTimeMillis(); 62 | example5.shrinkList(list, 10); 63 | System.out.println((System.currentTimeMillis() - startMs) + "ms"); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/chapter_08/Example5.java: -------------------------------------------------------------------------------- 1 | package chapter_08; 2 | 3 | /** 4 | * 在方法/函数中提前返回 5 | * 最小化嵌套 6 | * 7 | * @author biezhi 8 | * @date 2018/7/11 9 | */ 10 | public class Example5 { 11 | 12 | public boolean contains(String str, String subString) { 13 | if (str == null || subString == null) { 14 | return false; 15 | } 16 | if (subString.equals("")) { 17 | return true; 18 | } 19 | return str.indexOf(subString) != -1; 20 | } 21 | 22 | final int SUCCESS = 200; 23 | 24 | // public void part1() { 25 | // int userResult = 200; 26 | // int permissionResult = 300; 27 | // 28 | // String message = ""; 29 | // if (userResult == SUCCESS) { 30 | // if (permissionResult != SUCCESS) { 31 | // message = "没有权限阅读"; 32 | // return; 33 | // } 34 | // message = ""; 35 | // } else { 36 | // message = String.valueOf(userResult); 37 | // } 38 | // // 模拟使用 message 39 | // System.out.println(message); 40 | // } 41 | 42 | public void part2() { 43 | int userResult = 200; 44 | int permissionResult = 300; 45 | 46 | String message = ""; 47 | if(userResult != SUCCESS){ 48 | message = String.valueOf(userResult); 49 | // 模拟使用 message 50 | System.out.println(message); 51 | return; 52 | } 53 | if (permissionResult != SUCCESS) { 54 | message = "没有权限阅读"; 55 | // 模拟使用 message 56 | System.out.println(message); 57 | return; 58 | } 59 | message = ""; 60 | // 模拟使用 message 61 | System.out.println(message); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/chapter_10/Example5.java: -------------------------------------------------------------------------------- 1 | package chapter_10; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * 把定义向下移 11 | * 12 | * @author biezhi 13 | * @date 2018/7/25 14 | */ 15 | public class Example5 { 16 | 17 | private static final int MAX_SPAM_VOTES = 10; 18 | 19 | private Map messages = new HashMap<>(); 20 | private Map> replies = new HashMap<>(); 21 | 22 | class Message { 23 | int viewCount; 24 | LocalDateTime lastViewTime; 25 | 26 | public void save() { 27 | // do save 28 | } 29 | } 30 | 31 | class Reply { 32 | int spamVotes; 33 | } 34 | 35 | // public List viewFilterdReplies(Long originalId) { 36 | // List filteredReplies = new ArrayList<>(); 37 | // Message rootMessage = messages.get(originalId); 38 | // List allReplies = replies.get(originalId); 39 | // 40 | // rootMessage.viewCount += 1; 41 | // rootMessage.lastViewTime = LocalDateTime.now(); 42 | // rootMessage.save(); 43 | // 44 | // for (Reply reply : allReplies) { 45 | // if (reply.spamVotes > MAX_SPAM_VOTES) { 46 | // filteredReplies.add(reply); 47 | // } 48 | // } 49 | // return filteredReplies; 50 | // } 51 | 52 | public List viewFilterdReplies(Long originalId) { 53 | 54 | Message rootMessage = messages.get(originalId); 55 | rootMessage.viewCount += 1; 56 | rootMessage.lastViewTime = LocalDateTime.now(); 57 | rootMessage.save(); 58 | 59 | List filteredReplies = new ArrayList<>(); 60 | List allReplies = replies.get(originalId); 61 | for (Reply reply : allReplies) { 62 | if (reply.spamVotes > MAX_SPAM_VOTES) { 63 | filteredReplies.add(reply); 64 | } 65 | } 66 | return filteredReplies; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/chapter_12/Example2.java: -------------------------------------------------------------------------------- 1 | package chapter_12; 2 | 3 | /** 4 | * 从对象中抽取值 5 | * 6 | * @author biezhi 7 | * @date 2018/8/25 8 | */ 9 | public class Example2 { 10 | 11 | static class Address { 12 | String countryName; 13 | String provinceName; 14 | String cityName; 15 | String areaName; 16 | 17 | public Address(String countryName, String provinceName, String cityName, String areaName) { 18 | this.countryName = countryName; 19 | this.provinceName = provinceName; 20 | this.cityName = cityName; 21 | this.areaName = areaName; 22 | } 23 | } 24 | 25 | // 上海 - 浦东 26 | // public String part1(Address address) { 27 | // String place = address.cityName; 28 | // if (place == null) { 29 | // place = address.provinceName; 30 | // } 31 | // if (place == null && "中国".equals(address.countryName)) { 32 | // place = address.countryName; 33 | // } 34 | // if (place == null) { 35 | // place = "未知城市"; 36 | // } 37 | // if (address.areaName != null) { 38 | // place += " - " + address.areaName; 39 | // } else { 40 | // place += " - 无人区"; 41 | // } 42 | // return place; 43 | // } 44 | 45 | // 上海 - 浦东 46 | public String part2(Address address) { 47 | String provinceName = address.provinceName; 48 | String cityName = address.cityName; 49 | String areaName = address.areaName; 50 | 51 | String firstHalf = ifNullThen("未知城市", cityName, provinceName); 52 | String secondHalf = ifNullThen("无人区", areaName); 53 | // || 54 | return firstHalf + " - " + secondHalf; 55 | } 56 | 57 | private String ifNullThen(String defaultValue, String...args){ 58 | if(null == args || args.length == 0){ 59 | return defaultValue; 60 | } 61 | for (String arg : args) { 62 | if(arg != null){ 63 | return arg; 64 | } 65 | } 66 | return defaultValue; 67 | } 68 | 69 | public static void main(String[] args) { 70 | Example2 example2 = new Example2(); 71 | String place = example2.part2( 72 | new Address("中国", null, "上海", "徐汇区") 73 | ); 74 | place = example2.part2( 75 | new Address("中国", "山西省", null, "迎泽区") 76 | ); 77 | System.out.println(place); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/chapter_14/access.log.txt: -------------------------------------------------------------------------------- 1 | 2018/09/24 13:12:49 INFO [worker@threadㄧ5] c.b.s.n.HttpServerDispatcher: 200 25ms GET /static/css/style.min.css 2 | 2018/09/24 13:12:49 INFO [worker@threadㄧ6] c.b.s.n.HttpServerDispatcher: 200 23ms GET /static/plugins/jquery/3.2.1/jquery.min.js 3 | 2018/09/24 13:12:49 INFO [worker@threadㄧ4] c.b.s.n.HttpServerDispatcher: 404 25ms GET /static/plugins/vue-loading/vue-loading.min.css 4 | 2018/09/24 13:12:49 INFO [worker@threadㄧ2] c.b.s.n.HttpServerDispatcher: 200 29ms GET /static/plugins/bootstrap/3.3.7/css/bootstrap.min.css 5 | 2018/09/24 13:12:49 INFO [worker@threadㄧ3] c.b.s.n.HttpServerDispatcher: 404 27ms GET /static/plugins/limonte-sweetalert2/6.4.1/sweetalert2.min.css 6 | 2018/09/24 13:12:49 INFO [worker@threadㄧ5] c.b.s.n.HttpServerDispatcher: 200 1ms GET /static/plugins/vue/vue.min.js 7 | 2018/09/24 13:12:49 INFO [worker@threadㄧ4] c.b.s.n.HttpServerDispatcher: 200 1ms GET /static/plugins/bootstrap/3.3.7/js/bootstrap.min.js 8 | 2018/09/24 13:12:49 INFO [worker@threadㄧ1] c.b.s.n.HttpServerDispatcher: 200 29ms GET /static/plugins/jquery-steps/1.1.0/jquery.steps.css 9 | 2018/09/24 13:12:49 INFO [worker@threadㄧ3] c.b.s.n.HttpServerDispatcher: 404 1ms GET /static/plugins/limonte-sweetalert2/6.4.1/sweetalert2.min.js 10 | 2018/09/24 13:12:49 INFO [worker@threadㄧ6] c.b.s.n.HttpServerDispatcher: 200 1ms GET /static/plugins/vue-loading/vue-loading.min.js 11 | 2018/09/24 13:12:49 INFO [worker@threadㄧ1] c.b.s.n.HttpServerDispatcher: 200 1ms GET /static/plugins/axios/axios.min.js 12 | 2018/09/24 13:12:49 INFO [worker@threadㄧ2] c.b.s.n.HttpServerDispatcher: 200 3ms GET /static/plugins/jquery-validate/1.15.1/jquery.validate.min.js 13 | 2018/09/24 13:12:49 INFO [worker@threadㄧ4] c.b.s.n.HttpServerDispatcher: 200 2ms GET /static/plugins/jquery-validate/1.15.1/localization/messages_zh.min.js 14 | 2018/09/24 13:12:49 INFO [worker@threadㄧ5] c.b.s.n.HttpServerDispatcher: 200 1ms GET /static/js/base.js 15 | 2018/09/24 13:12:49 INFO [worker@threadㄧ4] c.b.s.n.HttpServerDispatcher: 404 25ms GET /static/plugins/vue-loading/vue-loading.min.css 16 | 2018/09/24 13:12:49 INFO [worker@threadㄧ6] c.b.s.n.HttpServerDispatcher: 200 1ms GET /static/plugins/jquery-steps/1.1.0/jquery.steps.min.js 17 | 2018/09/24 13:12:49 INFO [worker@threadㄧ3] c.b.s.n.HttpServerDispatcher: 200 1ms GET /static/js/install.js 18 | 2018/09/24 13:12:49 INFO [worker@threadㄧ1] c.b.s.n.HttpServerDispatcher: 200 2ms GET /robots.txt 19 | 2018/09/24 13:12:49 INFO [worker@threadㄧ1] c.b.s.n.HttpServerDispatcher: 200 2ms GET /static/images/bg/5.png 20 | 2018/09/24 13:12:50 INFO [worker@threadㄧ1] c.b.s.n.HttpServerDispatcher: 500 2ms GET /index -------------------------------------------------------------------------------- /src/main/java/chapter_05/Example5.java: -------------------------------------------------------------------------------- 1 | package chapter_05; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * 把代码分成段落 11 | * 12 | * @author biezhi 13 | * @date 2018/7/3 14 | */ 15 | public class Example5 { 16 | 17 | private String findGameAccount(String username){ 18 | return null; 19 | } 20 | 21 | private String findAccountArea(String account){ 22 | return null; 23 | } 24 | 25 | public List part1(List users){ 26 | List accounts = users.stream().map(this::findGameAccount).collect(Collectors.toList()); 27 | Map accountArea = accounts.stream().collect(Collectors.toMap((account) -> account, this::findAccountArea)); 28 | Set> entries = accountArea.entrySet(); 29 | List successUsers = new ArrayList<>(); 30 | // for (Map.Entry entry: entries) { 31 | // try { 32 | // System.out.println("给 ["+ entry.getKey() +"] ["+ entry.getValue() +"] 发送游戏道具"); 33 | // System.out.println("给 ["+ entry.getKey() +"] 发送邮件通知"); 34 | // successUsers.add(entry.getKey()); 35 | // } catch (Exception e){ 36 | // e.printStackTrace(); 37 | // } 38 | // } 39 | System.out.println("2018-07-03 发送了["+ successUsers.size() +"]个 xxx 游戏道具"); 40 | return successUsers; 41 | } 42 | 43 | public List part2(List users){ 44 | // 查找游戏账号和大区 45 | List accounts = users.stream().map(this::findGameAccount).collect(Collectors.toList()); 46 | Map accountArea = accounts.stream() 47 | .collect(Collectors.toMap((account) -> account, this::findAccountArea)); 48 | 49 | // 给账号发送道具,并发送邮件通知 50 | List successUsers = accountArea.entrySet().stream() 51 | .map(this::sendGameProp).collect(Collectors.toList()); 52 | 53 | // 记录日志 54 | System.out.println("2018-07-03 发送了["+ successUsers.size() +"]个 xxx 游戏道具"); 55 | return successUsers; 56 | } 57 | 58 | private String sendGameProp(Map.Entry accountEntry){ 59 | try { 60 | System.out.println("给 ["+ accountEntry.getKey() +"] ["+ accountEntry.getValue() +"] 发送游戏道具"); 61 | System.out.println("给 ["+ accountEntry.getKey() +"] 发送邮件通知"); 62 | return accountEntry.getKey(); 63 | } catch (Exception e){ 64 | throw new RuntimeException(e); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/chapter_13/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_13; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 用自然语言描述解决方案 8 | * 9 | * @author biezhi 10 | * @date 2018/9/1 11 | */ 12 | public class Example3 { 13 | 14 | class Movie { 15 | Integer id; 16 | String title; 17 | String coverImage; 18 | String intro; 19 | } 20 | 21 | class CurrentIndex { 22 | int title = 0; 23 | int cover = 0; 24 | int intro = 0; 25 | } 26 | 27 | private List findAll(String sql) { 28 | return new ArrayList<>(); 29 | } 30 | 31 | public void printMoive() { 32 | List titles = findAll("SELECT id, title FROM movie1 ORDER BY id ASC"); 33 | List coverImages = findAll("SELECT id, cover_image FROM movie2 ORDER BY id ASC"); 34 | List intros = findAll("SELECT id, intro FROM movie3 ORDER BY id ASC"); 35 | 36 | CurrentIndex currentIndex = new CurrentIndex(); 37 | 38 | for (Movie movie : titles) { 39 | 40 | boolean matched = advanceToMatchIndex(currentIndex, titles, coverImages, intros); 41 | if(!matched){ 42 | break; 43 | } 44 | System.out.print("id: " + titles.get(currentIndex.title).id); 45 | System.out.print(", title: " + titles.get(currentIndex.title).title); 46 | System.out.print(", cover: " + coverImages.get(currentIndex.cover).coverImage); 47 | System.out.print(", intro: " + intros.get(currentIndex.intro).intro); 48 | System.out.print("\n"); 49 | 50 | currentIndex.title++; 51 | currentIndex.cover++; 52 | currentIndex.intro++; 53 | } 54 | 55 | } 56 | 57 | private boolean advanceToMatchIndex(CurrentIndex currentIndex, 58 | List titles, 59 | List coverImages, 60 | List intros){ 61 | 62 | for (Movie title : titles) { 63 | int titleId = titles.get(currentIndex.title).id; 64 | int coverId = coverImages.get(currentIndex.cover).id; 65 | int introId = intros.get(currentIndex.intro).id; 66 | 67 | if(titleId == coverId && titleId == introId){ 68 | return true; 69 | } 70 | 71 | int maxId = getMaxId(titleId, coverId, introId); 72 | if(titleId < maxId){ 73 | currentIndex.title++; 74 | } 75 | if(coverId < maxId){ 76 | currentIndex.cover++; 77 | } 78 | if(introId < maxId){ 79 | currentIndex.intro++; 80 | } 81 | } 82 | 83 | return false; 84 | } 85 | 86 | private Integer getMaxId(Integer...ids){ 87 | return ids[0] > ids[1] ? 88 | Math.max(ids[0], ids[2]) : Math.max(ids[1], ids[2]); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | 24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | ### JetBrains template 27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 29 | 30 | # User-specific stuff 31 | .idea/**/workspace.xml 32 | .idea/**/tasks.xml 33 | .idea/**/dictionaries 34 | .idea/**/shelf 35 | 36 | # Sensitive or high-churn files 37 | .idea/**/dataSources/ 38 | .idea/**/dataSources.ids 39 | .idea/**/dataSources.local.xml 40 | .idea/**/sqlDataSources.xml 41 | .idea/**/dynamic.xml 42 | .idea/**/uiDesigner.xml 43 | .idea/**/dbnavigator.xml 44 | 45 | # Gradle 46 | .idea/**/gradle.xml 47 | .idea/**/libraries 48 | 49 | # CMake 50 | cmake-build-debug/ 51 | cmake-build-release/ 52 | 53 | # Mongo Explorer plugin 54 | .idea/**/mongoSettings.xml 55 | 56 | # File-based project format 57 | *.iws 58 | 59 | # IntelliJ 60 | out/ 61 | 62 | # mpeltonen/sbt-idea plugin 63 | .idea_modules/ 64 | 65 | # JIRA plugin 66 | atlassian-ide-plugin.xml 67 | 68 | # Cursive Clojure plugin 69 | .idea/replstate.xml 70 | 71 | # Crashlytics plugin (for Android Studio and IntelliJ) 72 | com_crashlytics_export_strings.xml 73 | crashlytics.properties 74 | crashlytics-build.properties 75 | fabric.properties 76 | 77 | # Editor-based Rest Client 78 | .idea/httpRequests 79 | ### Maven template 80 | target/ 81 | pom.xml.tag 82 | pom.xml.releaseBackup 83 | pom.xml.versionsBackup 84 | pom.xml.next 85 | release.properties 86 | dependency-reduced-pom.xml 87 | buildNumber.properties 88 | .mvn/timing.properties 89 | 90 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 91 | !/.mvn/wrapper/maven-wrapper.jar 92 | ### Eclipse template 93 | 94 | .metadata 95 | bin/ 96 | tmp/ 97 | *.tmp 98 | *.bak 99 | *.swp 100 | *~.nib 101 | local.properties 102 | .settings/ 103 | .loadpath 104 | .recommenders 105 | 106 | # External tool builders 107 | .externalToolBuilders/ 108 | 109 | # Locally stored "Eclipse launch configurations" 110 | *.launch 111 | 112 | # PyDev specific (Python IDE for Eclipse) 113 | *.pydevproject 114 | 115 | # CDT-specific (C/C++ Development Tooling) 116 | .cproject 117 | 118 | # CDT- autotools 119 | .autotools 120 | 121 | # Java annotation processor (APT) 122 | .factorypath 123 | 124 | # PDT-specific (PHP Development Tools) 125 | .buildpath 126 | 127 | # sbteclipse plugin 128 | .target 129 | 130 | # Tern plugin 131 | .tern-project 132 | 133 | # TeXlipse plugin 134 | .texlipse 135 | 136 | # STS (Spring Tool Suite) 137 | .springBeans 138 | 139 | # Code Recommenders 140 | .recommenders/ 141 | 142 | # Scala IDE specific (Scala & Java development for Eclipse) 143 | .cache-main 144 | .scala_dependencies 145 | .worksheet 146 | ### VisualStudioCode template 147 | .vscode/* 148 | !.vscode/settings.json 149 | !.vscode/tasks.json 150 | !.vscode/launch.json 151 | !.vscode/extensions.json 152 | 153 | .idea/ 154 | *.iml -------------------------------------------------------------------------------- /src/main/java/chapter_12/Example3.java: -------------------------------------------------------------------------------- 1 | package chapter_12; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 大一点的例子 8 | * 9 | * @author biezhi 10 | * @date 2018/8/25 11 | */ 12 | public class Example3 { 13 | 14 | private Map> counts = new HashMap<>(); 15 | 16 | class EventLog { 17 | String exitState; 18 | 19 | boolean hasExitState() { 20 | return exitState != null; 21 | } 22 | 23 | } 24 | 25 | class HttpHeaders { 26 | int code; 27 | String contentType; 28 | 29 | boolean hasResponseCode() { 30 | return true; 31 | } 32 | 33 | boolean hasContentType() { 34 | return true; 35 | } 36 | } 37 | 38 | class HttpDownload { 39 | EventLog eventLog; 40 | HttpHeaders httpHeaders; 41 | 42 | boolean hasEventLog() { 43 | return eventLog != null; 44 | } 45 | 46 | boolean hasHttpHeaders() { 47 | return httpHeaders != null; 48 | } 49 | } 50 | 51 | void updateCounts1(HttpDownload hd) { 52 | // counts["exitState"][hd.exitState()]++; "SUCCESS" | "FAILURE" 53 | // counts["httpStatus"][hd.statusCode()]++; "404" | "200" 54 | // counts["contentType"][hd.contentType()]++; "text/html" | "application/json" 55 | } 56 | 57 | // void updateCounts2(HttpDownload hd) { 58 | // if (!hd.hasEventLog() || !hd.eventLog.hasExitState()) { 59 | // int plus = 0; // 计算当前值+1 60 | // counts.get("exitState").putIfAbsent("unknown", plus); 61 | // } else { 62 | // int plus = 0; 63 | // String state = hd.eventLog.exitState; 64 | // counts.get("exitState").putIfAbsent(state, plus); 65 | // } 66 | // 67 | // if (!hd.hasHttpHeaders()) { 68 | // int plus = 0; 69 | // counts.get("httpStatus").putIfAbsent("unknown", plus); 70 | // counts.get("contentType").putIfAbsent("unknown", plus); 71 | // return; 72 | // } 73 | // 74 | // HttpHeaders httpHeaders = hd.httpHeaders; 75 | // 76 | // // 记录 httpStatus 的计数,不存在则记录为 unknown 77 | // if (!httpHeaders.hasResponseCode()) { 78 | // int plus = 0; // 计算当前值+1 79 | // counts.get("httpStatus").putIfAbsent("unknown", plus); 80 | // } else { 81 | // int plus = 0; 82 | // String code = String.valueOf(httpHeaders.code); 83 | // counts.get("httpStatus").putIfAbsent(code, plus); 84 | // } 85 | // 86 | // // 记录 contentType 的计数,不存在则记录为 unknown 87 | // if (!httpHeaders.hasContentType()) { 88 | // int plus = 0; 89 | // counts.get("contentType").putIfAbsent("unknown", plus); 90 | // } else { 91 | // int plus = 0; 92 | // String contentType = httpHeaders.contentType; 93 | // counts.get("contentType").putIfAbsent(contentType, plus); 94 | // } 95 | // } 96 | 97 | void updateCounts3(HttpDownload hd) { 98 | String exitState = "unknown"; 99 | String httpStatus = "unknown"; 100 | String contentType = "unknown"; 101 | 102 | if (hd.hasEventLog() && hd.eventLog.hasExitState()) { 103 | exitState = hd.eventLog.exitState; 104 | } 105 | 106 | if(hd.hasHttpHeaders() && hd.httpHeaders.hasResponseCode()){ 107 | httpStatus = String.valueOf(hd.httpHeaders.code); 108 | } 109 | 110 | if(hd.hasHttpHeaders() && hd.httpHeaders.hasContentType()){ 111 | contentType = hd.httpHeaders.contentType; 112 | } 113 | 114 | int plus = 0; // 计算当前值+1 115 | counts.get("exitState").putIfAbsent(exitState, plus); 116 | counts.get("httpStatus").putIfAbsent(httpStatus, plus); 117 | counts.get("contentType").putIfAbsent(contentType, plus); 118 | } 119 | 120 | } 121 | --------------------------------------------------------------------------------