mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-26 21:26:41 +08:00
706 lines
55 KiB
HTML
706 lines
55 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>076 程序员练级攻略(2018):软件设计.md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li><a href="../">上一级</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/000 开篇词 洞悉技术的本质,享受科技的乐趣.md.html">000 开篇词 洞悉技术的本质,享受科技的乐趣</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/001 程序员如何用技术变现(上).md.html">001 程序员如何用技术变现(上)</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/002 程序员如何用技术变现(下).md.html">002 程序员如何用技术变现(下)</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/003 Equifax信息泄露始末.md.html">003 Equifax信息泄露始末</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/004 从Equifax信息泄露看数据安全.md.html">004 从Equifax信息泄露看数据安全</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/005 何为技术领导力.md.html">005 何为技术领导力</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/006 如何拥有技术领导力.md.html">006 如何拥有技术领导力</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/007 推荐阅读:每个程序员都该知道的事.md.html">007 推荐阅读:每个程序员都该知道的事</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/008 Go语言,Docker和新技术.md.html">008 Go语言,Docker和新技术</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/009 答疑解惑:渴望、热情和选择.md.html">009 答疑解惑:渴望、热情和选择</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/010 如何成为一个大家愿意追随的Leader?.md.html">010 如何成为一个大家愿意追随的Leader?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/011 程序中的错误处理:错误返回码和异常捕捉.md.html">011 程序中的错误处理:错误返回码和异常捕捉</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/012 程序中的错误处理:异步编程和最佳实践.md.html">012 程序中的错误处理:异步编程和最佳实践</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/013 魔数 0x5f3759df.md.html">013 魔数 0x5f3759df</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/014 推荐阅读:机器学习101.md.html">014 推荐阅读:机器学习101</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/015 时间管理:同扭曲时间的事儿抗争.md.html">015 时间管理:同扭曲时间的事儿抗争</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/016 时间管理:投资赚取时间.md.html">016 时间管理:投资赚取时间</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/017 故障处理最佳实践:应对故障.md.html">017 故障处理最佳实践:应对故障</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/018 故障处理最佳实践:故障改进.md.html">018 故障处理最佳实践:故障改进</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/019 答疑解惑:我们应该能够识别的表象和本质.md.html">019 答疑解惑:我们应该能够识别的表象和本质</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/020 分布式系统架构的冰与火.md.html">020 分布式系统架构的冰与火</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/021 从亚马逊的实践,谈分布式系统的难点.md.html">021 从亚马逊的实践,谈分布式系统的难点</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/022 分布式系统的技术栈.md.html">022 分布式系统的技术栈</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/023 分布式系统关键技术:全栈监控.md.html">023 分布式系统关键技术:全栈监控</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/024 分布式系统关键技术:服务调度.md.html">024 分布式系统关键技术:服务调度</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/025 分布式系统关键技术:流量与数据调度.md.html">025 分布式系统关键技术:流量与数据调度</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/026 洞悉PaaS平台的本质.md.html">026 洞悉PaaS平台的本质</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/027 推荐阅读:分布式系统架构经典资料.md.html">027 推荐阅读:分布式系统架构经典资料</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/028 编程范式游记(1)- 起源.md.html">028 编程范式游记(1)- 起源</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/029 编程范式游记(2)- 泛型编程.md.html">029 编程范式游记(2)- 泛型编程</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/030 编程范式游记(3) - 类型系统和泛型的本质.md.html">030 编程范式游记(3) - 类型系统和泛型的本质</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/031 Git协同工作流,你该怎样选.md.html">031 Git协同工作流,你该怎样选</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/032 推荐阅读:分布式数据调度相关论文.md.html">032 推荐阅读:分布式数据调度相关论文</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/033 编程范式游记(4)- 函数式编程.md.html">033 编程范式游记(4)- 函数式编程</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/034 编程范式游记(5)- 修饰器模式.md.html">034 编程范式游记(5)- 修饰器模式</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/035 编程范式游记(6)- 面向对象编程.md.html">035 编程范式游记(6)- 面向对象编程</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/036 编程范式游记(7)- 基于原型的编程范式.md.html">036 编程范式游记(7)- 基于原型的编程范式</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/037 编程范式游记(8)- Go 语言的委托模式.md.html">037 编程范式游记(8)- Go 语言的委托模式</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/038 编程范式游记(9)- 编程的本质.md.html">038 编程范式游记(9)- 编程的本质</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/039 编程范式游记(10)- 逻辑编程范式.md.html">039 编程范式游记(10)- 逻辑编程范式</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/040 编程范式游记(11)- 程序世界里的编程范式.md.html">040 编程范式游记(11)- 程序世界里的编程范式</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/041 弹力设计篇之“认识故障和弹力设计”.md.html">041 弹力设计篇之“认识故障和弹力设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/042 弹力设计篇之“隔离设计”.md.html">042 弹力设计篇之“隔离设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/043 弹力设计篇之“异步通讯设计”.md.html">043 弹力设计篇之“异步通讯设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/044 弹力设计篇之“幂等性设计”.md.html">044 弹力设计篇之“幂等性设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/045 弹力设计篇之“服务的状态”.md.html">045 弹力设计篇之“服务的状态”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/046 弹力设计篇之“补偿事务”.md.html">046 弹力设计篇之“补偿事务”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/047 弹力设计篇之“重试设计”.md.html">047 弹力设计篇之“重试设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/048 弹力设计篇之“熔断设计”.md.html">048 弹力设计篇之“熔断设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/049 弹力设计篇之“限流设计”.md.html">049 弹力设计篇之“限流设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/050 弹力设计篇之“降级设计”.md.html">050 弹力设计篇之“降级设计”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/051 弹力设计篇之“弹力设计总结”.md.html">051 弹力设计篇之“弹力设计总结”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/052 区块链技术 - 区块链的革命性及技术概要.md.html">052 区块链技术 - 区块链的革命性及技术概要</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/053 区块链技术 - 区块链技术细节 - 哈希算法.md.html">053 区块链技术 - 区块链技术细节 - 哈希算法</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/054 区块链技术 - 区块链技术细节 - 加密和挖矿.md.html">054 区块链技术 - 区块链技术细节 - 加密和挖矿</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/055 区块链技术 - 去中心化的共识机制.md.html">055 区块链技术 - 去中心化的共识机制</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/056 区块链技术 - 智能合约.md.html">056 区块链技术 - 智能合约</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/057 区块链技术 - 传统金融和虚拟货币.md.html">057 区块链技术 - 传统金融和虚拟货币</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/058 管理设计篇之分布式锁.md.html">058 管理设计篇之分布式锁</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/059 管理设计篇之配置中心.md.html">059 管理设计篇之配置中心</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/060 管理设计篇之边车模式.md.html">060 管理设计篇之边车模式</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/061 管理设计篇之服务网格.md.html">061 管理设计篇之服务网格</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/062 管理设计篇之网关模式.md.html">062 管理设计篇之网关模式</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/063 管理设计篇之部署升级策略.md.html">063 管理设计篇之部署升级策略</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/064 性能设计篇之缓存.md.html">064 性能设计篇之缓存</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/065 性能设计篇之异步处理.md.html">065 性能设计篇之异步处理</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/066 性能设计篇之数据库扩展.md.html">066 性能设计篇之数据库扩展</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/067 性能设计篇之秒杀.md.html">067 性能设计篇之秒杀</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/068 性能设计篇之边缘计算.md.html">068 性能设计篇之边缘计算</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/069 程序员练级攻略(2018):开篇词.md.html">069 程序员练级攻略(2018):开篇词</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/070 程序员练级攻略(2018):零基础启蒙.md.html">070 程序员练级攻略(2018):零基础启蒙</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/071 程序员练级攻略(2018):正式入门.md.html">071 程序员练级攻略(2018):正式入门</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/072 程序员练级攻略(2018):程序员修养.md.html">072 程序员练级攻略(2018):程序员修养</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/073 程序员练级攻略(2018):编程语言.md.html">073 程序员练级攻略(2018):编程语言</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/074 程序员练级攻略:理论学科.md.html">074 程序员练级攻略:理论学科</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/075 程序员练级攻略(2018):系统知识.md.html">075 程序员练级攻略(2018):系统知识</a>
|
||
</li>
|
||
<li>
|
||
<a class="current-tab" href="/专栏/左耳听风/076 程序员练级攻略(2018):软件设计.md.html">076 程序员练级攻略(2018):软件设计</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/077 程序员练级攻略(2018):Linux系统、内存和网络.md.html">077 程序员练级攻略(2018):Linux系统、内存和网络</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/078 程序员练级攻略(2018):异步IO模型和Lock-Free编程.md.html">078 程序员练级攻略(2018):异步IO模型和Lock-Free编程</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/079 程序员练级攻略(2018):Java底层知识.md.html">079 程序员练级攻略(2018):Java底层知识</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/080 程序员练级攻略(2018):数据库.md.html">080 程序员练级攻略(2018):数据库</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/081 程序员练级攻略(2018):分布式架构入门.md.html">081 程序员练级攻略(2018):分布式架构入门</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/082 程序员练级攻略(2018):分布式架构经典图书和论文.md.html">082 程序员练级攻略(2018):分布式架构经典图书和论文</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/083 程序员练级攻略(2018):分布式架构工程设计.md.html">083 程序员练级攻略(2018):分布式架构工程设计</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/084 程序员练级攻略(2018):微服务.md.html">084 程序员练级攻略(2018):微服务</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/085 程序员练级攻略(2018):容器化和自动化运维.md.html">085 程序员练级攻略(2018):容器化和自动化运维</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/086 程序员练级攻略(2018):机器学习和人工智能.md.html">086 程序员练级攻略(2018):机器学习和人工智能</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/087 程序员练级攻略(2018):前端基础和底层原理.md.html">087 程序员练级攻略(2018):前端基础和底层原理</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/088 程序员练级攻略(2018):前端性能优化和框架.md.html">088 程序员练级攻略(2018):前端性能优化和框架</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/089 程序员练级攻略(2018):UIUX设计.md.html">089 程序员练级攻略(2018):UIUX设计</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/090 程序员练级攻略(2018):技术资源集散地.md.html">090 程序员练级攻略(2018):技术资源集散地</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/091 程序员面试攻略:面试前的准备.md.html">091 程序员面试攻略:面试前的准备</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/092 程序员面试攻略:面试中的技巧.md.html">092 程序员面试攻略:面试中的技巧</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/093 程序员面试攻略:面试风格.md.html">093 程序员面试攻略:面试风格</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/094 程序员面试攻略:实力才是王中王.md.html">094 程序员面试攻略:实力才是王中王</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/095 高效学习:端正学习态度.md.html">095 高效学习:端正学习态度</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/096 高效学习:源头、原理和知识地图.md.html">096 高效学习:源头、原理和知识地图</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/097 高效学习:深度,归纳和坚持实践.md.html">097 高效学习:深度,归纳和坚持实践</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/098 高效学习:如何学习和阅读代码.md.html">098 高效学习:如何学习和阅读代码</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/099 高效学习:面对枯燥和量大的知识.md.html">099 高效学习:面对枯燥和量大的知识</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/100 高效沟通:Talk和Code同等重要.md.html">100 高效沟通:Talk和Code同等重要</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/101 高效沟通:沟通阻碍和应对方法.md.html">101 高效沟通:沟通阻碍和应对方法</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/102 高效沟通:沟通方式及技巧.md.html">102 高效沟通:沟通方式及技巧</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/103 高效沟通:沟通技术.md.html">103 高效沟通:沟通技术</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/104 高效沟通:好老板要善于提问.md.html">104 高效沟通:好老板要善于提问</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/105 高效沟通:好好说话的艺术.md.html">105 高效沟通:好好说话的艺术</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/106 加餐 谈谈我的“三观”.md.html">106 加餐 谈谈我的“三观”</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/107 结束语 业精于勤,行成于思.md.html">107 结束语 业精于勤,行成于思</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
<div class="sidebar-toggle-inner"></div>
|
||
</div>
|
||
<script>
|
||
function add_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>076 程序员练级攻略(2018):软件设计</h1>
|
||
<p>学习软件设计的方法、理念、范式和模式,是让你从一个程序员通向工程师的必备技能。如果你不懂这些设计方法,那么你将无法做出优质的软件。这就好像写作文一样,文章人人都能写,但是能写得有条理,有章法,有血有肉,就不简单了。软件开发也一样,实现功能,做出来并不难,但是要做漂亮,做优雅,就非常不容易了。</p>
|
||
<p>Linus 说过,这世界程序员之所有高下之分,最大的区别就是程序员的“品味”不一样。有品位的程序员和没有品位的程序员所写出来的代码,所做出来的软件,差距非常大,而且价值也差别很大。<strong>所以,对于我们每一个程序员,如果你想成为软件工程师、设计师或架构师,软件设计是你必须用心学习的事</strong>。</p>
|
||
<p>然而,软件设计这个事,并不是一朝一夕就能学会的,也不是别人能把你教会的,很多东西需要你自己用实践、用时间、用错误、用教训、用痛苦才能真正体会其中的精髓的。所以,除了学习理论知识外,你还需要大量的工程实践,然后每过一段时间就把这些设计的东西重新回炉一下。你会发现这些软件设计的东西,就像饮茶一样,一开始是苦的,然后慢慢回甘,最终你会喝出真正的滋味。</p>
|
||
<p><strong>要学好这些软件开发和设计的方法,你真的需要磨练和苦行,反复咀嚼,反复推敲,在实践和理论中螺旋式地学习,才能真正掌握。</strong> 所以,你需要有足够的耐心和恒心。</p>
|
||
<h1>编程范式</h1>
|
||
<p>学习编程范式可以让你明白编程的本质和各种语言的编程方式。虽然很多程序员都忽略了这个事,但是其实是非常非常重要的事。因此,我推荐以下一些资料,帮助你系统化地学习和理解。</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://en.wikipedia.org/wiki/Programming_paradigm">Wikipedia: Programming paradigm</a> ,维基百科上有一个编程范式的页面,顺着这个页面看下去,你可以看到很多很多有用的和编程相关的知识。这些东西对你的编程技能的提高会非常非常有帮助。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://www.ybrikman.com/writing/2014/04/09/six-programming-paradigms-that-will/">Six programming paradigms that will change how you think about coding</a>,中文翻译版为 <a href="https://my.oschina.net/editorial-story/blog/890965">六个编程范型将改变你对编程的看法</a>。这篇文章讲了默认支持并发(Concurrent by default)、依赖类型(Dependent types)、连接性语言(Concatenative languages)、声明式编程(Declarative programming)、符号式编程(Symbolic programming)、基于知识的编程(Knowledge-based programming)等六种不太常见的编程范式,并结合了一些你没怎么听说过的语言来分别进行讲述。</p>
|
||
<p>比如在讲 Concatenative languages 时,以 Forth、cat 和 joy 三种语言为例讲述这一编程范式背后的思想——语言中的所有内容都是一个函数,用于将数据推送到堆栈或从堆栈弹出数据;程序几乎完全通过功能组合来构建(concatenation is composition)。作者认为,这些编程范式背后的思想十分有魅力,能够改变对编程的思考。我看完此文,对此也深信不疑。虽然这些语言和编程范式不常用到,但确实能在思想层面给予人很大的启发。这也是我推荐此文的目的。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://www.info.ucl.ac.be/~pvr/VanRoyChapter.pdf">Programming Paradigms for Dummies: What Every Programmer Should Know</a> ,这篇文章的作者彼得·范·罗伊(Peter Van Roy)是比利时鲁汶大学的计算机科学教师。他在这篇文章里分析了编程语言在历史上的演进,有哪些典型的、值得研究的案例,里面体现了哪些值得学习的范式。</p>
|
||
<p>比如,在分布式编程领域,他提到了 Erlang、E、Distributed Oz 和 Didactic Oz 这四种编程语言。虽然它们都是分布式编程语言,但各有特色,各自解决了不同的问题。通过这篇文章能学到不少在设计编程语言时要考虑的问题,让你重新审视自己所使用的编程语言应该怎样用才能用好,有什么局限性,这些局限性能否被克服等。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://open.163.com/special/opencourse/paradigms.html">斯坦福大学公开课:编程范式</a>,这是一门比较基础且很详细的课程,适合学习编程语言的初学者。它通过讲述 C、C++、并发编程、Scheme、Python 这 5 门语言,介绍了它们各自不同的编程范式。以 C 语言为例,它解释了 C 语言的基本要素,如指针、内存分配、堆、C 风格的字符串等,并解释了为什么 C 语言会在泛型编程、多态等方面有局限性。通过学习这门课程,你会对一些常用的编程范式有所了解。</p>
|
||
</li>
|
||
</ul>
|
||
<h1>一些软件设计的相关原则</h1>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Don’t Repeat Yourself (DRY)</strong> ,DRY 是一个最简单的法则,也是最容易被理解的。但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易的事)。它意味着,当在两个或多个地方发现一些相似的代码的时候,我们需要把它们的共性抽象出来形成一个唯一的新方法,并且改变现有地方的代码让它们以一些合适的参数调用这个新的方法。</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Keep It Simple, Stupid(KISS)</strong> ,KISS 原则在设计上可能最被推崇,在家装设计、界面设计和操作设计上,复杂的东西越来越被众人所鄙视了,而简单的东西越来越被人所认可。宜家(IKEA)简约、高效的家居设计和生产思路;微软(Microsoft)“所见即所得”的理念;谷歌(Google)简约、直接的商业风格,无一例外地遵循了“KISS”原则。也正是“KISS”原则,成就了这些看似神奇的商业经典。而苹果公司的 iPhone 和 iPad 将这个原则实践到了极至。</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Program to an interface, not an implementation</strong>,这是设计模式中最根本的哲学,注重接口,而不是实现,依赖接口,而不是实现。接口是抽象是稳定的,实现则是多种多样的。在面向对象的 S.O.L.I.D 原则中会提到我们的依赖倒置原则,就是这个原则的另一种样子。还有一条原则叫 Composition over inheritance(喜欢组合而不是继承),这两条是那 23 个经典设计模式中的设计原则。</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>You Ain’t Gonna Need It (YAGNI)</strong> ,这个原则简而言之为——只考虑和设计必须的功能,避免过度设计。只实现目前需要的功能,在以后你需要更多功能时,可以再进行添加。如无必要,勿增复杂性。软件开发是一场 trade-off 的博弈。</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Law of Demeter</strong>,迪米特法则 (Law of Demeter),又称“最少知识原则”(Principle of Least Knowledge),其来源于 1987 年荷兰大学的一个叫做 Demeter 的项目。克雷格·拉尔曼(Craig Larman)把 Law of Demeter 又称作“不要和陌生人说话”。在《程序员修炼之道》中讲 LoD 的那一章将其叫作“解耦合与迪米特法则”。</p>
|
||
<p>关于迪米特法则有一些很形象的比喻:1) 如果你想让你的狗跑的话,你会对狗狗说还是对四条狗腿说?2) 如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿?和狗的四肢说话?让店员自己从钱包里拿钱?这听起来有点儿荒唐,不过在我们的代码里这几乎是见怪不怪的事情了。对于 LoD,正式的表述如下:</p>
|
||
<p>对于对象 ‘O’ 中一个方法’M’,M 应该只能够访问以下对象中的方法:</p>
|
||
<ol>
|
||
<li>对象 O;</li>
|
||
<li>与 O 直接相关的 Component Object;</li>
|
||
<li>由方法 M 创建或者实例化的对象;</li>
|
||
<li>作为方法 M 的参数的对象。</li>
|
||
</ol>
|
||
</li>
|
||
<li>
|
||
<p><strong>面向对象的 S.O.L.I.D 原则">http://en.wikipedia.org/wiki/Solid_(object-oriented_design)</strong>:</p>
|
||
<ul>
|
||
<li><strong>SRP(Single Responsibility Principle)- 职责单一原则</strong>。关于单一职责原则,其核心的思想是:一个类,只做一件事,并把这件事做好,其只有一个引起它变化的原因。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。</li>
|
||
</ul>
|
||
<p>职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而极大地损伤其内聚性和耦合度。单一职责,通常意味着单一的功能,因此不要为一个模块实现过多的功能点,以保证实体只有一个引起它变化的原因。</p>
|
||
<ul>
|
||
<li><strong>OCP(Open/Closed Principle)- 开闭原则</strong>。关于开发封闭原则,其核心的思想是:模块是可扩展的,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。</li>
|
||
<li><strong>LSP(Liskov substitution principle)- 里氏代换原则</strong>。软件工程大师罗伯特·马丁(Robert C. Martin)把里氏代换原则最终简化为一句话:“Subtypes must be substitutable for their base types”。也就是,子类必须能够替换成它们的基类。即子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。另外,不应该在代码中出现 if/else 之类对子类类型进行判断的条件。里氏替换原则 LSP 是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。</li>
|
||
<li><strong>ISP(Interface Segregation Principle )- 接口隔离原则</strong>。接口隔离原则的意思是把功能实现在接口中,而不是类中,使用多个专门的接口比使用单一的总接口要好。举个例子,我们对电脑有不同的使用方式,比如:写作、通讯、看电影、打游戏、上网、编程、计算和数据存储等。</li>
|
||
</ul>
|
||
<p>如果我们把这些功能都声明在电脑的抽象类里面,那么,我们的上网本、PC 机、服务器和笔记本的实现类都要实现所有的这些接口,这就显得太复杂了。所以,我们可以把这些功能接口隔离开来,如工作学习接口、编程开发接口、上网娱乐接口、计算和数据服务接口,这样,我们的不同功能的电脑就可以有所选择地继承这些接口。</p>
|
||
<ul>
|
||
<li><strong>DIP(Dependency Inversion Principle)- 依赖倒置原则</strong>。高层模块不应该依赖于低层模块的实现,而是依赖于高层抽象。举个例子,墙面的开关不应该依赖于电灯的开关实现,而是应该依赖于一个抽象的开关的标准接口。这样,当我们扩展程序的时候,开关同样可以控制其它不同的灯,甚至不同的电器。也就是说,电灯和其它电器继承并实现我们的标准开关接口,而开关厂商就可以不需要关于其要控制什么样的设备,只需要关心那个标准的开关标准。这就是依赖倒置原则。</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://c2.com/cgi/wiki?CommonClosurePrinciple">CCP(Common Closure Principle) - 共同封闭原则</a>,一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类。一个更简短的说法是:一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,那么我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。</p>
|
||
<p>CCP 原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果两个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。CCP 延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://c2.com/cgi/wiki?CommonReusePrinciple">CRP(Common Reuse Principle)- 共同重用原则</a> ,包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换个说法是,没有被一起重用的类不应该组合在一起。CRP 原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。</p>
|
||
<p>当一个包发生了改变,并发布新的版本,使用这个包的所有用户都必须在新的包环境下验证他们的工作,即使被他们使用的部分没有发生任何改变。因为如果包中包含未被使用的类,即使用户不关心该类是否改变,但用户还是不得不升级该包并对原来的功能加以重新测试。CCP 则让系统的维护者受益。CCP 让包尽可能大(CCP 原则加入功能相关的类),CRP 则让包尽可能小(CRP 原则剔除不使用的类)。它们的出发点不一样,但不相互冲突。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://en.wikipedia.org/wiki/Hollywood_Principle">好莱坞原则 - Hollywood Principle</a> ,好莱坞原则就是一句话——“don’t call us, we’ll call you.”。意思是,好莱坞的经纪人不希望你去联系他们,而是他们会在需要的时候来联系你。也就是说,所有的组件都是被动的,所有的组件初始化和调用都由容器负责。</p>
|
||
<p>简单来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:1) 不创建对象,而是描述创建对象的方式。2)在代码中,对象与服务没有直接联系,而是容器负责将这些联系在一起。控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。好莱坞原则就是<a href="https://en.wikipedia.org/wiki/Inversion_of_Control">IoC(Inversion of Control)</a> 或<a href="https://martinfowler.com/articles/injection.html">DI(Dependency Injection)</a>的基础原则。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://en.wikipedia.org/wiki/Coupling_(computer_science)">高内聚, 低耦合 & - High Cohesion & Low/Loose coupling</a>,这个原则是 UNIX 操作系统设计的经典原则,把模块间的耦合降到最低,而努力让一个模块做到精益求精。内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。对于面向对象来说,你也可以看看马萨诸塞州戈登学院的面向对象课中的这一节讲义<a href="http://www.math-cs.gordon.edu/courses/cs211/lectures-2009/Cohesion,Coupling,MVC.pdf">High Cohesion and Low Coupling</a>。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://en.wikipedia.org/wiki/Convention_over_Configuration">CoC(Convention over Configuration)- 惯例优于配置原则</a> ,简单点说,就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如,Hibernate 的映射文件,如果约定字段名和类属性一致的话,基本上就可以不要这个配置文件了。你的应用只需要指定不 convention 的信息即可,从而减少了大量 convention 而又不得不花时间和精力啰里啰嗦的东东。</p>
|
||
<p>配置文件在很多时候相当影响开发效率。Rails 中很少有配置文件(但不是没有,数据库连接就是一个配置文件)。Rails 的 fans 号称其开发效率是 Java 开发的 10 倍,估计就是这个原因。Maven 也使用了 CoC 原则,当你执行 <code>mvn -compile</code> 命令的时候,不需要指定源文件放在什么地方,而编译以后的 class 文件放置在什么地方也没有指定,这就是 CoC 原则。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://sulong.me/archives/99">SoC (Separation of Concerns) - 关注点分离</a> ,SoC 是计算机科学中最重要的努力目标之一。这个原则,就是在软件开发中,通过各种手段,将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题,就是相对较易解决的。问题太过于复杂,要解决问题需要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。</p>
|
||
<p>正如程序员的记忆力相对于计算机知识来说那么有限一样,程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候,如果我们把所有的东西混在一起讨论,那么就只会有一个结果——乱。实现关注点分离的方法主要有两种,一种是标准化,另一种是抽象与包装。标准化就是制定一套标准,让使用者都遵守它,将人们的行为统一起来,这样使用标准的人就不用担心别人会有很多种不同的实现,使自己的程序不能和别人的配合。</p>
|
||
<p>就像是开发镙丝钉的人只专注于开发镙丝钉就行了,而不用关注镙帽是怎么生产的,反正镙帽和镙丝钉按照标准来就一定能合得上。不断地把程序的某些部分抽象并包装起来,也是实现关注点分离的好方法。一旦一个函数被抽象出来并实现了,那么使用函数的人就不用关心这个函数是如何实现的。同样的,一旦一个类被抽象并实现了,类的使用者也不用再关注于这个类的内部是如何实现的。诸如组件、分层、面向服务等这些概念都是在不同的层次上做抽象和包装,以使得使用者不用关心它的内部实现细节。</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://en.wikipedia.org/wiki/Design_by_contract">DbC(Design by Contract)- 契约式设计</a> ,DbC 的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。如果在程序设计中一个模块提供了某种功能,那么它要:</p>
|
||
<ul>
|
||
<li>期望所有调用它的客户模块都保证一定的进入条件:这就是模块的先验条件(客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况)。</li>
|
||
<li>保证退出时给出特定的属性:这就是模块的后验条件(供应商的义务,显然也是客户的权利)。</li>
|
||
<li>在进入时假定,并在退出时保持一些特定的属性:不变式。</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple">ADP(Acyclic Dependencies Principle)- 无环依赖原则</a> ,包(或服务)之间的依赖结构必须是一个直接的无环图形,也就是说,在依赖结构中不允许出现环(循环依赖)。如果包的依赖形成了环状结构,怎么样打破这种循环依赖呢?</p>
|
||
<p>有两种方法可以打破这种循环依赖关系:第一种方法是创建新的包,如果 A、B、C 形成环路依赖,那么把这些共同类抽出来放在一个新的包 D 里。这样就把 C 依赖 A 变成了 C 依赖 D 以及 A 依赖 D,从而打破了循环依赖关系。第二种方法是使用 DIP(依赖倒置原则)和 ISP(接口分隔原则)设计原则。无环依赖原则(ADP)为我们解决包之间的关系耦合问题。在设计模块时,不能有循环依赖。</p>
|
||
</li>
|
||
</ul>
|
||
<h1>一些软件设计的读物</h1>
|
||
<ul>
|
||
<li>《<a href="https://book.douban.com/subject/26819666/">领域驱动设计</a>》 ,本书是领域驱动设计方面的经典之作。全书围绕着设计和开发实践,结合若干真实的项目案例,向读者阐述如何在真实的软件开发中应用领域驱动设计。书中给出了领域驱动设计的系统化方法,并将人们普遍接受的一些实践综合到一起,融入了作者的见解和经验,展现了一些可扩展的设计新实践、已验证过的技术以及便于应对复杂领域的软件项目开发的基本原则。</li>
|
||
<li>《<a href="https://book.douban.com/subject/1467587/">UNIX 编程艺术</a>》 ,这本书主要介绍了 Unix 系统领域中的设计和开发哲学、思想文化体系、原则与经验,由公认的 Unix 编程大师、开源运动领袖人物之一埃里克·雷蒙德(Eric S. Raymond)倾力多年写作而成。包括 Unix 设计者在内的多位领域专家也为本书贡献了宝贵的内容。本书内容涉及社群文化、软件开发设计与实现,覆盖面广、内容深邃,完全展现了作者极其深厚的经验积累和领域智慧。</li>
|
||
<li>《<a href="https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html">Clean Architecture</a>》,如果你读过 《<a href="https://book.douban.com/subject/5442024/">Clean Code</a>》 和 《<a href="https://book.douban.com/subject/11614538/">The Clean Coder</a>》这两本书。你就能猜得到这种 Clean 系列一定也是出自“Bob 大叔”之手。没错,就是 Bob 大叔的心血之作。除了这个网站,《<a href="https://book.douban.com/subject/26915970/">Clean Architecture</a>》也是一本书,这是一本很不错的架构类图书。对软件架构的元素、方法等讲得很清楚。示例都比较简单,并带一些软件变化历史的讲述,很开阔视野。</li>
|
||
<li><a href="https://12factor.net/">The Twelve-Factor App</a> ,如今,软件通常会作为一种服务来交付,它们被称为网络应用程序,或软件即服务(SaaS)。12-Factor 为构建 SaaS 应用提供了方法论,这也是架构师必读的文章。(<a href="https://12factor.net/zh_cn/">中译版</a>) 这篇文章在业内的影响力很大,必读!</li>
|
||
<li><a href="https://medium.com/@rdsubhas/10-modern-software-engineering-mistakes-bc67fbef4fc8">Avoid Over Engineering</a> ,有时候,我们会过渡设计我们的系统,过度设计会把我们带到另外一个复杂度上,所以,我们需要一些工程上的平衡。这篇文章是一篇非常不错地告诉你什么是过度设计的文章。</li>
|
||
<li><a href="https://medium.com/@DataStax/instagram-engineerings-3-rules-to-a-scalable-cloud-application-architecture-c44afed31406">Instagram Engineering’s 3 rules to a scalable cloud application architecture</a> ,Instagram 工程的三个黄金法则:1)使用稳定可靠的技术(迎接新的技术);2)不要重新发明轮子;3)Keep it very simple。我觉得这三条很不错。其实,Amazon 也有两条工程法则,一个是自动化,一个是简化。</li>
|
||
<li><a href="https://www.infoq.com/presentations/effective-api-design">How To Design A Good API and Why it Matters - Joshua Bloch</a> ,Google 的一个分享,关于如何设计好一个 API。</li>
|
||
<li>关于 Restful API 的设计,你可以学习并借鉴一下下面这些文章。
|
||
<ul>
|
||
<li><a href="https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api">Best Practices for Designing a Pragmatic RESTful API</a></li>
|
||
<li><a href="https://betimdrenica.wordpress.com/2015/03/09/ideal-rest-api-design/">Ideal REST API design</a></li>
|
||
<li><a href="https://github.com/interagent/http-api-design">HTTP API Design Guide</a></li>
|
||
<li><a href="https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md.html">Microsoft REST API Guidelines</a></li>
|
||
<li><a href="https://github.com/watson-developer-cloud/api-guidelines">IBM Watson REST API Guidelines</a></li>
|
||
<li><a href="https://opensource.zalando.com/restful-api-guidelines/">Zalando RESTful API and Event Scheme Guidelines</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="https://blog.codinghorror.com/the-problem-with-logging/">The Problem With Logging</a> ,一篇关于程序打日志的短文,可以让你知道一些可能以往不知道的打日志需要注意的问题。</li>
|
||
<li><a href="https://berb.github.io/diploma-thesis/community/index.html">Concurrent Programming for Scalable Web Architectures</a> ,这是一本在线的免费书,教你如何架构一个可扩展的高性能的网站。其中谈到了一些不错的设计方法和知识。</li>
|
||
</ul>
|
||
<h1>小结</h1>
|
||
<p>好了,总结一下今天分享的内容。我认为,“品位”不同,是各层次程序员之间最大的区别,这也决定了他们所做出来的软件的质量和价值。因此,我特意撰写了软件设计这一篇章,帮助那些想成长为软件工程师、设计师或架构师的程序员,提高软件设计的品位,进而实现自己的目标。</p>
|
||
<p>虽然很多程序员都忽略了对编程范式的学习,但我觉得学习编程范式其实是非常非常重要的事,能够明白编程的本质和各种语言的编程方式。为此,我推荐了好几份学习资料,帮助你系统化地学习和理解。随后我介绍了 DRY- 避免重复原则、KISS- 简单原则、迪米特法则(又称“最少知识原则”)、 面向对象的 S.O.L.I.D 原则等多个经典的软件设计原则。</p>
|
||
<p>最后,我精选并推荐了软件设计方面的学习资料,如《领域驱动设计》、《UNIX 编程艺术》和《Clean Architecture》等必读好书,以及如何构建 SaaS,如何避免过度设计,如何设计 API,如何用程序打日志等方面的资料。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/左耳听风/075 程序员练级攻略(2018):系统知识.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/左耳听风/077 程序员练级攻略(2018):Linux系统、内存和网络.md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997881ae573cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
</script>
|
||
</html>
|