<el-input style="width: 300px;" v-model="form.accountPhone"
oninput="value=value.replace(/[^0-9]/g,'')"
maxlength="11" placeholder="请输入手机号" clearable></el-input>
Web
常用的翻页逻辑,以uview2为例
<template>
<view class="u-page">
<u-list @scrolltolower="scrolltolower">
<view v-for="(item, index) in modelArr" :key="index">
<view style="width:100%;height:100px;">{{item.slug}}</view>
</view>
</u-list>
</view>
</template>
<script>
export default {
data() {
return {
modelArr: [],
page:1,
}
},
onLoad() {
this.clearAndRefreshData()
},
methods: {
clearAndRefreshData() {
this.page = 1
this.requestData()
},
requestData() {
const uri = "wp/v2/posts"
const param = {page:this.page,per_page:10}
getApp().get(uri,param).then(res => {
if (this.page == 1) {
this.modelArr = res
}else{
this.modelArr = this.modelArr.concat(res)
}
if (res.length > 0){
this.page = this.page + 1
}
}).catch(err => {
console.log(err)
})
},
scrolltolower() {
this.requestData()
},
}
}
</script>
<style>
</style>
1.PHP入门篇
1.1 PHP可以做什么?
1.2 认识PHP代码标识
1.3 PHP-Echo语句
1.4 PHP-计算表达式
1.5 PHP-字符串
1.6 PHP语句结束符
1.7 PHP中的注释
2.1 WAMPServer集成环境安装
3.1 PHP-什么是变量
3.2 PHP-如何定义变量
3.3 PHP-变量名的命名规则
3.4 PHP-什么是变量的数据类型
3.5 PHP-变量的数据类型
3.6 PHP标量类型—布尔类型
3.7 PHP标量类型—整型
3.8 PHP标量类型—浮点型
3.9 PHP标量类型—字符串(1)
3.10 PHP标量类型—字符串(2)
3.11 PHP标量类型—字符串(3)
3.12 PHP标量类型—字符串(4)
3.13 PHP第一种特殊类型—资源
3.14 PHP第二种特殊类型—空类型
4.1 PHP-什么是常量
4.2 PHP-常量的作用
4.3 PHP-认识一下系统常量
4.4 PHP-常量如何取值
4.5 PHP-如何判定常量是否被定义
5.1 什么是运算符
5.2 PHP中的算术运算符
5.3 PHP中的赋值运算符
5.4 PHP中的比较运算符
5.5 PHP中的三元运算符
5.6 PHP中的逻辑运算符
5.7 PHP中的字符串连接运算符
5.8 PHP中的错误控制运算符
5.9 PHP中取模算术符的实际应用
6.1 PHP-顺序结构
6.2 PHP条件结构之if…else…
6.3 PHP条件结构之switch…case…
6.4 PHP中循环结构之while循环语句
6.5 PHP中循环结构之do while循环语句
6.6 PHP中循环结构之for循环语句
6.7 PHP中循环结构之foreach循环语句
7.1 Zend Studio的安装
7.2 创建一个PHP项目
7.3 Zend Studio-常用快捷键
7.4 代码调试功能
7.5 Zend Studio其他一些常用功能
为什么有些程序员宁愿降薪也要离开创业公司?
本文共分为四个部分:
案例:4人招聘团队,1个月,0入职
趋势:资本寒冬带来的市场异动
现状:创业公司的招聘窘境
干货:初创企业找人的正确姿势
案例:4人招聘团队,1个月,0入职
先说一个小道消息吧,现在互联网人才市场上最紧俏的两个岗位,一个是程序员,一个新媒体。新媒体难到什么程度,我不清楚,但招一个好的程序员多难,我是有概念的。
前段时间调研了一家创业公司,他们要招3名后端开发的程序员。这家公司已经Pre-A轮了,暂时不差钱那种,需要扩充研发团队,他们当时投入了4个行政实习生,花了一个月,从各个渠道(包括猎头)找到了500份简历,筛出了大概100份符合要求的,但最后成功约面只有30人左右,发出了1个offer,但这个唯一的offer被求职者拒绝了。
所以整个过程总结起来就是:他们投入了4个人力,花了1个月时间,实际产出了0个入职。
当然,很多人觉得这家公司傲娇,500份简历筛出一个人来,要求这么高,活该招不到人,但实际上做过招聘的人都知道,500份看起来还行的简历,仔细一筛,真剩不下几份合适能用的,不过这是题外话了。
再给个简单的数据,我们是做高端程序员职位推荐的,所有求职者注册的时候都需要选择期望公司的轮次,下面的数据是北京地区被推荐过的程序员对公司轮次的选择:
这其中明确表示能接受天使轮和A轮的程序员分别只占5.3%,8.8%,而明确要求D轮以上和上市公司的候选人,则达到了19.7%和19.2%。
并且,注意两个信息:
这里的样本是北京地区,求职者对创业公司的接受度远高于其它地区(尤其是二线城市);
接受天使轮和A轮的同样也接受D轮以上或上市,反之则不然。这个数据意味着大多数有丰富工作经验的程序员选择依然偏向保守。
所以这段时间,不靠谱的创业公司会发现,自己拿不到融资了;靠谱的创业公司则会发现,自己拿到融资也招不到人了。
异动:程序员降薪也要离开创业公司
当然,创业公司招人从来都不是容易事,某种程度上,创业堪比上山结寇,这种事情拉人入伙从古至今,就没有容易一说。
然后,最近这几个月,创业公司面临的招聘环境尤其糟糕。
大概是从2个多月之前开始,我在后台查看简历的时候,会频频看到「上一家公司倒闭了/融资困难/已经2个月没发工资了……」这样的离职理由,频率非常高,其中涉及的行业相当广泛,从金融到社交,从约车平台到奢侈品电商,无一不足(甚至还有招聘领域的公司)。
于是,上周在某互联网招聘平台上整理了一百家公开的A轮前HR邮箱,发了一封测试邮件,发现有接近40%的邮箱后缀已经变成了无效域名,也就是说这40%的公司极有可能已经阵亡了。
在创业最火热的时候,市场一片欣欣向荣,去创业公司,往往意味着薪水翻翻、title变高、逼格满满,真的,那种双倍薪水挖大公司里平庸程序员的案例见得多了。不得不说,那时候,整个市场的舆论风向对创业公司都是有利的。但潮水在加速后退,裸泳的人正在暴露。
而求职者对大环境的变化永远都是敏感的,见过一个案例,是一个安卓程序员,在某网约车做了一年多,独立负责这家公司产品的安卓开发,当初是被几乎翻翻的薪资挖去这家公司的,但今年年中这家公司就解散了,在辗转了一家创业公司后,如今,这名程序员又在找工作,并且主动提出可以降低薪水,但条件是公司要成熟稳定。
一名水平还不错的程序员主动提出降薪跳槽,这当然是极端情况,但正因为极端,才反映了市场的异动。
现状:创业公司的招聘窘境
回到创业公司招聘本身,资本寒冬带来的异动是浮出表面的结果,水面之下实际上还有更大的冰山。
一:猎头不愿意接中小创业公司的单
和大多数人印象不一样,创业公司虽然花钱谨慎,但很多公司都有针对招聘人才的预算,单人成本甚至不比大公司低多少,但不论是在所谓的猎头聚合平台,还是传统猎头,都不愿意接单,也就是有钱也花不出去。
为什么?因为他们也没有大量的优质简历来源,对他们来说,单个职位的成单效率直接决定了收入,而创业公司的单个职位成单效率,不客气起说,低得令人发指。
对中小创业公司来说,得到接近猎头的服务,一大可能性来自于,猎头提供初步的意向沟通,形成鲜活的人才池,供企业自行邀约。这考验的是招聘渠道对大数据的收集、整理和筛选能力,以及自身招聘顾问基于经验对各种不可控因素的把控,传统猎头很难把上下游都整合好。
二:初创公司连好的简历来源都没有
这里要重申一下互联网带给的一大幻觉,人们总以为互联网能给草根带来更有质量的信息和更平等的话语权。以招聘行业为例,不少人甚至以为,有了个各种各样的免费渠道,草根创业公司就跟大公司平等了。
但免费的才是最贵的,这是亘古不变的真理,互联网的信息流通成本降低,带来的是更加海量的垃圾信息,而大公司往往已经建立起了很高的信息门槛,草根就变成了承担垃圾信息流通成本的洼地。
所以在对公司和求职者都充分开放的招聘渠道,基本只能解决非常基础职位需求,稍微高端一点的人才,有各种猎头天天盯着,很少会主动投递简历;另外,移动互联网的赛事都进入下半场了,招聘行业的移动化依然乏善可陈,就是因为提供纯工具服务的招聘平台同样无法解决信息高效匹配,这跟你用PC端操作还是手机操作没有关系;拍卖是这几年针对中高端人才兴起的新的招聘模式,但其基本逻辑是优化候选人的跳槽体验,对B端实际上更苛刻,这种模式下,即使小而美的中小创业公司也毫无优势。
如果把招聘比成一场恋爱,那创业公司得到的最高定位,实际上就是备胎。
三:初创公司的到面率很低
到面率也可以理解成已经约好面试却被放鸽子的概率。这个概率低到什么程度不好说,因为各个公司不一样,但绝对是普遍存在的问题。对HR来说,过了约好时间的半个小时,你打电话过去,小心翼翼地询问对方是不是「堵车了」,得到的回答常常是轻描淡写地一句「不来了!」都懒得委婉一下。
没有做过招聘的人很难理解,对中小创业公司来说,一个应聘者哪怕只是到面,花费的成本实际上已经很高了。
四:HR没有招聘经验
招聘经验有多重要,这么说吧,一位成熟的HR知道招聘是一个系统工程,从理解职位需求、判断业务价值,到渠道,到简历漏斗,到邀约候选人,他能充分判断每个环节的成本和产出,不断调整策略,供CEO做决定。而一个没有经验的HR会把招聘当成守株待兔的过程,全程懵逼,并把这种懵逼状态反馈给同样没有经验的CEO,双双陷入抓瞎。
干货:初创企业找人的正确姿势
一、随时随地,主动找人
我知道一个牛逼的案例,一家起步很高的技术型公司,员工总数只有20+人,但有一个5人规模的HR团队,其中还包括一名互联网招聘经历丰富的资深从业者,主动定向找人。大多数公司当然都不可能有这个实力,甚至可能只有一个还在实习的HR,这个HR还要兼职前台、财务和行政。但我也见过创业公司,连离职的实习生都敢挖前来采访的记者,并且还成功了。
二、CEO一定要参与面试!
如上所述,创业公司的到面率已经很低了,无论HR还是猎头千方百计争取到面,就是因为面试才是招聘方和应聘者发生化学反应的时刻。无论是在描述项目的商业本质还是讲故事的技巧上,CEO应该像对待投资人一样,对待应聘者。根据我们的过往案例,CEO参与面试的入职率无一例外要远远高于没有CEO参与的。
三、匹配的人才是最合适的,让项目跑起来再说
虽然大多数前辈专家会告诉你,花2倍的钱去招一个10倍产出的人,这个理论绝对没错,但如果你就是找不到这个人,或者你找到了这个人,人家就是不愿意来怎么办呢?因为现实情况就是,在招聘市场上,一个三年工作经验的及格线程序员愿意去创业公司就不错了。
有一个办法就是拆解自己的岗位去适应人才,让项目跑起来再说,项目跑不起来,「完美的人才」来了的时候你的公司可能已经死了。
说了这么多,其实总结起来,就是「永远把人放在战略核心的位置。」毕竟在寒冬,一些创业公司会因为错位的资金死去,另一些创业公司,却会因为人,而活下来。
程序员的出路之一 :重温 15 篇热文
本文精选了「伯乐在线」微信公号 12 月份的 15 篇热门文章,包括学习方法总结,程序员职场总结等。
注:以下文章,点击标题即可阅读
《程序员的出路之一 》
就现在经济大环境而言,很不乐观,程序员的日子也很不好过,无论是还在找工作的、还是已经入职多年、哪怕做到项目经理技术经理的,压力都异常巨大,似乎处处充满危机。但是,仔细分析一下,出路还是有的,甚至解决温饱、过上有房有车没贷款的生活也是很可能的。
《两种编程高手》
第一种软件工程师是天生的聪明人,他们处理复杂事物的能力是天生的,可以流利的心算三位数加三位数带进位的加法。
第二种工程师是天资正常的普通人,working memeory的容量大概就是可以心算两位数加两位数带进位的加法。
程序员靠开发应用养家糊口,每天的大部分时间都要趴在电脑前面写代码和重构代码。今天为大家介绍程序员最喜欢的十二款键盘。
我们生活在一个振奋人心的时代。我们可以越来越方便廉价地获得大量学习资源。这些资源的传播载体由最初的教室被变成了博客,技术论坛等。坐拥如此众多的学习资源,我们没有任何理由不去好好利用。
你第一次上计算机课的时候,有哪些难忘的事和大家分享一下。
谷歌是很多人梦寐以求的雇主公司,它是世界上最大、最有野心的技术公司,在这样一家公司工作还有什么好吐槽的呢?
然而事实就是这样,不信我们就来看看谷歌现有员工和前员工在 Quora 网站上的一个专门吐槽贴中说了些什么。
我本身是工程师出身,写代码至今已超过 15 年,创业也超过 10 年,曾经外包过很多项目给别人,也接过很多外包的项目。身处在创业圈,只要有创业朋友主动问过我的,我都会和他说如果你能不接就不要接。
我记不清我第一次看到一台电脑是什么时候了,但我确实记得当我被问到“长大以后你想做什么?”时,我的第一个念头是“在电脑上工作”。
若干年以后,我长大了成为了一个程序员。我扪心自问,“假设你还是一个小孩,当你长大后,你还想要在电脑上工作吗?
我们都有这样的业余项目:项目创建一半就弃之于坟墓,未完成的梦想遗体就这样扔之于文件夹中。游戏、应用程序、可视化数据等等。现在所谓伟大的点子最后只能沦落为空气中卑贱的尘埃。
很多时候总感觉梦想是如此遥不可及。明明知道该做什么,却总是感觉有无穷的困难在阻碍着自己。
到处都充满了竞,在你想做的事情上有着成千上万的人和你竞争。
成为优秀的程序员是极其困难的,并且这个过程不可能一蹴而就。
《软件工程师的困境》
我从本科系毕业,出社会第一份工作就是软件工程师,一路升到项目经理,创业后成为老板,当然也变成了 Sales,需要卖服务给更多的老板,以下就我看到不同的面向与大家分享软件工程师的困境。
程序猿,这个字汇在近几年开始渐渐被大众所熟知。在外界看来,这一直是个特殊的群体,社会上也给程序猿贴了很多的标签,内向、屌丝、苦逼、裤衩、拖鞋等等。在他们的心中,程序猿是这样的。
我来谈谈亲身经历过的“怪现象”。为什么说怪呢,人多力量大,似乎才符合常理,但是往往在软件项目开展的过程中会出现人多、事少、工作量大的情况,这跟我们以往的认知大相径庭。
工作的选择,求职的失败,是否继续深造等等,这些当时看起来事关重大的时刻,随着时间的流逝,态度的转变,一个人终究会泰然看待。
前端面试中的常见的算法问题
虽说我们很多时候前端很少有机会接触到算法。大多都交互性的操作,然而从各大公司面试来看,算法依旧是考察的一方面。实际上学习数据结构与算法对于工程师去理解和分析问题都是有帮助的。如果将来当我们面对较为复杂的问题,这些基础知识的积累可以帮助我们更好的优化解决思路。下面罗列在前端面试中经常撞见的几个问题吧。
Q1 判断一个单词是否是回文?
回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环。比如 mamam redivider .
很多人拿到这样的题目非常容易想到用for 将字符串颠倒字母顺序然后匹配就行了。其实重要的考察的就是对于reverse的实现。其实我们可以利用现成的函数,将字符串转换成数组,这个思路很重要,我们可以拥有更多的自由度去进行字符串的一些操作。
function checkPalindrom(str) {
return str == str.split(”).reverse().join(”);
}
Q2 去掉一组整型数组重复的值
比如输入: [1,13,24,11,11,14,1,2]
输出: [1,13,24,11,14,2]
需要去掉重复的11 和 1 这两个元素。
这道问题出现在诸多的前端面试题中,主要考察个人对Object的使用,利用key来进行筛选。
/**
* unique an array
**/
let unique = function(arr) {
let hashTable = {};
let data = [];
for(let i=0,l=arr.length;i<l;i++) {
if(!hashTable[arr[i]]) {
hashTable[arr[i]] = true;
data.push(arr[i]);
}
}
return data
}
module.exports = unique;
Q3 统计一个字符串出现最多的字母
给出一段英文连续的英文字符窜,找出重复出现次数最多的字母
输入 : afjghdfraaaasdenas
输出 : a
前面出现过去重的算法,这里需要是统计重复次数。
function findMaxDuplicateChar(str) {
if(str.length == 1) {
return str;
}
let charObj = {};
for(let i=0;i<str.length;i++) {
if(!charObj[str.charAt(i)]) {
charObj[str.charAt(i)] = 1;
}else{
charObj[str.charAt(i)] += 1;
}
}
let maxChar = ”,
maxValue = 1;
for(var k in charObj) {
if(charObj[k] >= maxValue) {
maxChar = k;
maxValue = charObj[k];
}
}
return maxChar;
}
module.exports = findMaxDuplicateChar;
Q4 排序算法
如果抽到算法题目的话,应该大多都是比较开放的题目,不限定算法的实现,但是一定要求掌握其中的几种,所以冒泡排序,这种较为基础并且便于理解记忆的算法一定需要熟记于心。冒泡排序算法就是依次比较大小,小的的大的进行位置上的交换。
function bubbleSort(arr) {
for(let i = 0,l=arr.length;i<l–1;i++) {
for(let j = i+1;j<l;j++) {
if(arr[i]>arr[j]) {
let tem = arr[i];
arr[i] = arr[j];
arr[j] = tem;
}
}
}
return arr;
}
module.exports = bubbleSort;
除了冒泡排序外,其实还有很多诸如 插入排序,快速排序,希尔排序等。每一种排序算法都有各自的特点。全部掌握也不需要,但是心底一定要熟悉几种算法。 比如快速排序,其效率很高,而其基本原理如图(来自wiki):
算法参考某个元素值,将小于它的值,放到左数组中,大于它的值的元素就放到右数组中,然后递归进行上一次左右数组的操作,返回合并的数组就是已经排好顺序的数组了。
function quickSort(arr) {
if(arr.length<=1) {
return arr;
}
let leftArr = [];
let rightArr = [];
let q = arr[0];
for(let i = 1,l=arr.length; i<l; i++) {
if(arr[i]>q) {
rightArr.push(arr[i]);
}else{
leftArr.push(arr[i]);
}
}
return [].concat(quickSort(leftArr),[q],quickSort(rightArr));
}
module.exports = quickSort;
安利大家一个学习的地址,通过动画演示算法的实现。
HTML5 Canvas Demo: Sorting Algorithms(http://math.hws.edu/eck/jsdemo/sortlab.html)
Q5 不借助临时变量,进行两个整数的交换
输入 a = 2, b = 4 输出 a = 4, b =2
这种问题非常巧妙,需要大家跳出惯有的思维,利用 a , b进行置换。
主要是利用 + – 去进行运算,类似 a = a + ( b – a) 实际上等同于最后 的 a = b;
function swap(a , b) {
b = b – a;
a = a + b;
b = a – b;
return [a,b];
}
module.exports = swap;
Q6 使用canvas 绘制一个有限度的斐波那契数列的曲线?
数列长度限定在9.
斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列主要考察递归的调用。我们一般都知道定义
fibo[i] = fibo[i-1]+fibo[i-2];
生成斐波那契数组的方法
function getFibonacci(n) {
var fibarr = [];
var i = 0;
while(i<n) {
if(i<=1) {
fibarr.push(i);
}else{
fibarr.push(fibarr[i–1] + fibarr[i–2])
}
i++;
}
return fibarr;
}
剩余的工作就是利用canvas arc方法进行曲线绘制了
DEMO(http://codepen.io/Jack_Pu/pen/LRaxZB)
Q7 找出下列正数组的最大差值比如:
输入 [10,5,11,7,8,9]
输出 6
这是通过一道题目去测试对于基本的数组的最大值的查找,很明显我们知道,最大差值肯定是一个数组中最大值与最小值的差。
function getMaxProfit(arr) {
var minPrice = arr[0];
var maxProfit = 0;
for (var i = 0; i < arr.length; i++) {
var currentPrice = arr[i];
minPrice = Math.min(minPrice, currentPrice);
var potentialProfit = currentPrice – minPrice;
maxProfit = Math.max(maxProfit, potentialProfit);
}
return maxProfit;
}
Q8 随机生成指定长度的字符串
实现一个算法,随机生成指制定长度的字符窜。
比如给定 长度 8 输出 4ldkfg9j
function randomString(n) {
let str = ‘abcdefghijklmnopqrstuvwxyz9876543210’;
let tmp = ”,
i = 0,
l = str.length;
for (i = 0; i < n; i++) {
tmp += str.charAt(Math.floor(Math.random() * l));
}
return tmp;
}
module.exports = randomString;
Q9 实现类似getElementsByClassName 的功能
自己实现一个函数,查找某个DOM节点下面的包含某个class的所有DOM节点?不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供DOM查找函数。
function queryClassName(node, name) {
var starts = ‘(^|[ \n\r\t\f])’,
ends = ‘([ \n\r\t\f]|$)’;
var array = [],
regex = new RegExp(starts + name + ends),
elements = node.getElementsByTagName(“*”),
length = elements.length,
i = 0,
element;
while (i < length) {
element = elements[i];
if (regex.test(element.className)) {
array.push(element);
}
i += 1;
}
return array;
}
Q10 使用JS 实现二叉查找树(Binary Search Tree)
一般叫全部写完的概率比较少,但是重点考察你对它的理解和一些基本特点的实现。 二叉查找树,也称二叉搜索树、有序二叉树(英语:ordered binary tree)是指一棵空树或者具有下列性质的二叉树:
- 任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点。二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。
在写的时候需要足够理解二叉搜素树的特点,需要先设定好每个节点的数据结构
class Node {
constructor(data, left, right) {
this.data = data;
this.left = left;
this.right = right;
}
}
树是有节点构成,由根节点逐渐延生到各个子节点,因此它具备基本的结构就是具备一个根节点,具备添加,查找和删除节点的方法.
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(data) {
let n = new Node(data, null, null);
if (!this.root) {
return this.root = n;
}
let currentNode = this.root;
let parent = null;
while (1) {
parent = currentNode;
if (data < currentNode.data) {
currentNode = currentNode.left;
if (currentNode === null) {
parent.left = n;
break;
}
} else {
currentNode = currentNode.right;
if (currentNode === null) {
parent.right = n;
break;
}
}
}
}
remove(data) {
this.root = this.removeNode(this.root, data)
}
removeNode(node, data) {
if (node == null) {
return null;
}
if (data == node.data) {
// no children node
if (node.left == null && node.right == null) {
return null;
}
if (node.left == null) {
return node.right;
}
if (node.right == null) {
return node.left;
}
let getSmallest = function(node) {
if(node.left === null && node.right == null) {
return node;
}
if(node.left != null) {
return node.left;
}
if(node.right !== null) {
return getSmallest(node.right);
}
}
let temNode = getSmallest(node.right);
node.data = temNode.data;
node.right = this.removeNode(temNode.right,temNode.data);
return node;
} else if (data < node.data) {
node.left = this.removeNode(node.left,data);
return node;
} else {
node.right = this.removeNode(node.right,data);
return node;
}
}
find(data) {
var current = this.root;
while (current != null) {
if (data == current.data) {
break;
}
if (data < current.data) {
current = current.left;
} else {
current = current.right
}
}
return current.data;
}
}
module.exports = BinarySearchTree;
完整代码 Github(https://github.com/JackPu/JavaScript-Algorithm-Learning)
不可不看的经典IT图书
出版社: 机械工业出版社
出版年: 2012-12
设计模式总结
从七月份开始一直到九月底才看完设计模式,在这个过程中我不敢说我已经掌握了那本书里面的内容,或者说1/5,没能力说也没有资格说。但是结果不重要,重要的是这个过程我的收获!主要包括如下几个方面:
1、认识了这么多设计模式。刚刚接触java没多久就在学长那里听过设计模式的大名,但是由于能力有限,一直不敢触碰。而今有幸将其都认识了。
2、开始有设计的理论了。在接触设计模式之前没有怎么想过设计方面东东,看到问题就立马动手解决,没有想到怎么样来设计更好,如何来是这块更加优化、漂亮。
3、开始考虑系统的可扩展性了。
4、在遇到问题后开始想有那个设计模式会适用这个场景。
5、对面向对象有了更深一步的了解。
鄙人天资不聪慧,既不是聪明人,更不是那种天才,所有顿悟有限!!!闲话过多,先看如下两幅图片
设计模式之间的关系:
设计模式总概况:
一、设计原则
1、单一职责原则
一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。from:百度百科
2、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
3、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。from:百度百科
4、依赖倒转原则(Dependence Inversion Principle)
所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
实现开闭原则的关键是抽象化,并且从抽象化导出具体化实现,如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要手段。 from:百度百科
5、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
6、合成复用原则(Composite Reuse Principle)
合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。
7、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。也就是说一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。
二、创建型模式
在软件工程中,创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。
创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合的方式。
创建型模式又分为对象创建型模式和类创建型模式。对象创建型模式处理对象的创建,类创建型模式处理类的创建。详细地说,对象创建型模式把对象创建的一部分推迟到另一个对象中,而类创建型模式将它对象的创建推迟到子类中。
1、抽象工厂模式(Abstract Factory)
所谓抽象工厂模式就是她提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。他允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。它的优点是隔离了具体类的生成,使得客户端不需要知道什么被创建了,而缺点就在于新增新的行为会比较麻烦,因为当添加一个新的产品对象时,需要更加需要更改接口及其下所有子类。其UML结构图如下:
参与者:
AbstractFactory:抽象工厂。抽象工厂定义了一个接口,所有的具体工厂都必须实现此接口,这个接口包含了一组方法用来生产产品。
ConcreteFactory:具体工厂。具体工厂是用于生产不同产品族。要创建一个产品,客户只需要使用其中一个工厂完全不需要实例化任何产品对象。
AbstractProduct:抽象产品。这是一个产品家族,每一个具体工厂都能够生产一整组产品。
Product:具体产品。
2、建造者模式(Builder)
对于建造者模式而已,它主要是将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。适用于那些产品对象的内部结构比较复杂。
建造者模式将复杂产品的构建过程封装分解在不同的方法中,使得创建过程非常清晰,能够让我们更加精确的控制复杂产品对象的创建过程,同时它隔离了复杂产品对象的创建和使用,使得相同的创建过程能够创建不同的产品。但是如果某个产品的内部结构过于复杂,将会导致整个系统变得非常庞大,不利于控制,同时若几个产品之间存在较大的差异,则不适用建造者模式,毕竟这个世界上存在相同点大的两个产品并不是很多,所以它的使用范围有限。其UML结构图:
Builder:抽象建造者。它声明为创建一个Product对象的各个部件指定的抽象接口。
ConcreteBuilder:具体建造者。实现抽象接口,构建和装配各个部件。
Director:指挥者。构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象,它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
Product:产品角色。一个具体的产品对象。
3、工厂方法模式(Factory Method)
作为抽象工厂模式的孪生兄弟,工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂方法模式让实例化推迟到子类。
工厂方法模式非常符合“开闭原则”,当需要增加一个新的产品时,我们只需要增加一个具体的产品类和与之对应的具体工厂即可,无须修改原有系统。同时在工厂方法模式中用户只需要知道生产产品的具体工厂即可,无须关系产品的创建过程,甚至连具体的产品类名称都不需要知道。虽然他很好的符合了“开闭原则”,但是由于每新增一个新产品时就需要增加两个类,这样势必会导致系统的复杂度增加。其UML结构图:
参与者:
Product:抽象产品。所有的产品必须实现这个共同的接口,这样一来,使用这些产品的类既可以引用这个接口。而不是具体类 。
ConcreteProduct:具体产品。
Creator:抽象工厂。它实现了所有操纵产品的方法,但不实现工厂方法。Creator所有的子类都必须要实现factoryMethod()方法。
ConcreteCreator:具体工厂。制造产品的实际工厂。它负责创建一个或者多个具体产品,只有ConcreteCreator类知道如何创建这些产品。
4、原型模式(Prototype)
在我们应用程序可能有某些对象的结构比较复杂,但是我们又需要频繁的使用它们,如果这个时候我们来不断的新建这个对象势必会大大损耗系统内存的,这个时候我们需要使用原型模式来对这个结构复杂又要频繁使用的对象进行克隆。所以原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
它主要应用与那些创建新对象的成本过大时。它的主要优点就是简化了新对象的创建过程,提高了效率,同时原型模式提供了简化的创建结构。UML结构图:
参与者:
Prototype:抽象原型类。声明克隆自身的接口。
ConcretePrototype:具体原型类。实现克隆的具体操作。
Client:客户类。让一个原型克隆自身,从而获得一个新的对象。
5、单例模式(Singleton)
单例模式,从字面上看就是一个实例的意思。所以它的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。
单例模式具备如下几个特点:
1、只有一个实例。
2、能够自我实例化。
3、提供全局访问点。
所以说当系统中只需要一个实例对象或者系统中只允许一个公共访问点,除了这个公共访问点外,不能通过其他访问点访问该实例时,可以使用单例模式。
单例模式的主要优点就是节约系统资源、提高了系统效率,同时也能够严格控制客户对它的访问。也许就是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,所以扩展起来有一定的困难。其UML结构图非常简单,就只有一个类:
参与者:
Singleton:单例。
三、结构型模式
结构型模式主要是用于处理类或者对象的组合,它描述了如何来类或者对象更好的组合起来,是从程序的结构上来解决模块之间的耦合问题。它主要包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式这个七个模式。
1、适配器模式(Adapter)
在我们的应用程序中我们可能需要将两个不同接口的类来进行通信,在不修改这两个的前提下我们可能会需要某个中间件来完成这个衔接的过程。这个中间件就是适配器。所谓适配器模式就是将一个类的接口,转换成客户期望的另一个接口。它可以让原本两个不兼容的接口能够无缝完成对接。
作为中间件的适配器将目标类和适配者解耦,增加了类的透明性和可复用性。
参与者:
Target:目标抽象类 。
Adapter:适配器类 。通过在内部包装一个Adaptee,将源接口转成目标接口。
Adaptee:适配者类 。需要适配的类。
Client:客户类。
2、桥接模式(Bridge)
如果说某个系统能够从多个角度来进行分类,且每一种分类都可能会变化,那么我们需要做的就是讲这多个角度分离出来,使得他们能独立变化,减少他们之间的耦合,这个分离过程就使用了桥接模式。所谓桥接模式就是讲抽象部分和实现部分隔离开来,使得他们能够独立变化。
桥接模式将继承关系转化成关联关系,封装了变化,完成了解耦,减少了系统中类的数量,也减少了代码量。
参与者
Abstraction:抽象类。
RefinedAbstraction:扩充抽象类。
Implementor:实现类接口。
ConcreteImplementor:具体实现类 。
3、组合模式(Composite)
组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。它定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理。
在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。
虽然组合模式能够清晰地定义分层次的复杂对象,也使得增加新构件也更容易,但是这样就导致了系统的设计变得更加抽象,如果系统的业务规则比较复杂的话,使用组合模式就有一定的挑战了。
参与者:
Component :组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
Leaf:叶子对象。叶子结点没有子结点。
Composite:容器对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
4、装饰者模式(Decorator)
我们可以通过继承和组合的方式来给一个对象添加行为,虽然使用继承能够很好拥有父类的行为,但是它存在几个缺陷:一、对象之间的关系复杂的话,系统变得复杂不利于维护。二、容易产生“类爆炸”现象。三、是静态的。在这里我们可以通过使用装饰者模式来解决这个问题。
装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。虽然装饰者模式能够动态将责任附加到对象上,但是他会产生许多的细小对象,增加了系统的复杂度。
参与者:
Component: 抽象构件。是定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent:具体构件。是定义了一个具体的对象,也可以给这个对象添加一些职责。
Decorator: 抽象装饰类。是装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。
ConcreteDecorator:具体装饰类,起到给Component添加职责的功能。
5、外观模式(Facade)
我们都知道类与类之间的耦合越低,那么可复用性就越好,如果两个类不必彼此通信,那么就不要让这两个类发生直接的相互关系,如果需要调用里面的方法,可以通过第三者来转发调用。外观模式非常好的诠释了这段话。外观模式提供了一个统一的接口,用来访问子系统中的一群接口。它让一个应用程序中子系统间的相互依赖关系减少到了最少,它给子系统提供了一个简单、单一的屏障,客户通过这个屏障来与子系统进行通信。
通过使用外观模式,使得客户对子系统的引用变得简单了,实现了客户与子系统之间的松耦合。但是它违背了“开闭原则”,因为增加新的子系统可能需要修改外观类或客户端的源代码。
参与者:
Facade: 外观角色。知道哪些子系统类负责处理请求,将客户的请求代理给适合的子系统处理。
SubSystem:子系统角色。实现子系统功能,处理Facade对象发来的请求。
6、享元模式(Flyweight)
在一个系统中对象会使得内存占用过多,特别是那些大量重复的对象,这就是对系统资源的极大浪费。享元模式对对象的重用提供了一种解决方案,它使用共享技术对相同或者相似对象实现重用。
享元模式就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。这里有一点要注意:享元模式要求能够共享的对象必须是细粒度对象。
享元模式通过共享技术使得系统中的对象个数大大减少了,同时享元模式使用了内部状态和外部状态,同时外部状态相对独立,不会影响到内部状态,所以享元模式能够使得享元对象在不同的环境下被共享。同时正是分为了内部状态和外部状态,享元模式会使得系统变得更加复杂,同时也会导致读取外部状态所消耗的时间过长。
参与者:
Flyweight: 抽象享元类。所有具体享元类的超类或者接口,通过这个接口,Flyweight可以接受并作用于外部专题。
ConcreteFlyweight: 具体享元类。指定内部状态,为内部状态增加存储空间。
UnsharedConcreteFlyweight: 非共享具体享元类。指出那些不需要共享的Flyweight子类。
FlyweightFactory: 享元工厂类。用来创建并管理Flyweight对象,它主要用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)。
7、代理模式(Proxy)、
代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉。
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的,同时也在一定程度上面减少了系统的耦合度。
参与者:
Subject: 抽象角色。声明真实对象和代理对象的共同接口。
Proxy: 代理角色。代理对象与真实对象实现相同的接口,所以它能够在任何时刻都能够代理真实对象。代理角色内部包含有对真实对象的引用,所以她可以操作真实对象,同时也可以附加其他的操作,相当于对真实对象进行封装。
RealSubject: 真实角色。它代表着真实对象,是我们最终要引用的对象。
四、行为型模式
行为型模式主要是用于描述类或者对象是怎样交互和怎样分配职责的。它涉及到算法和对象间的职责分配,不仅描述对象或者类的模式,还描述了他们之间的通信方式,它将你的注意力从控制流转移到了对象间的关系上来。行为型类模式采用继承机制在类间分派行为,而行为型对象模式使用对象复合而不是继承。它主要包括如何11中设计模式:职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。
1、职责链模式(Chain of Responsibility)
职责链模式描述的请求如何沿着对象所组成的链来传递的。它将对象组成一条链,发送者将请求发给链的第一个接收者,并且沿着这条链传递,直到有一个对象来处理它或者直到最后也没有对象处理而留在链末尾端。
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止,这就是职责链模式。在职责链模式中,使得每一个对象都有可能来处理请求,从而实现了请求的发送者和接收者之间的解耦。同时职责链模式简化了对象的结构,它使得每个对象都只需要引用它的后继者即可,而不必了解整条链,这样既提高了系统的灵活性也使得增加新的请求处理类也比较方便。但是在职责链中我们不能保证所有的请求都能够被处理,而且不利于观察运行时特征。
参与者:
Handler: 抽象处理者。定义了一个处理请求的方法。所有的处理者都必须实现该抽象类。
ConcreteHandler: 具体处理者。处理它所负责的请求,同时也可以访问它的后继者。如果它能够处理该请求则处理,否则将请求传递到它的后继者。
Client: 客户类。
2、命令模式(Command)
有些时候我们想某个对象发送一个请求,但是我们并不知道该请求的具体接收者是谁,具体的处理过程是如何的,们只知道在程序运行中指定具体的请求接收者即可,对于这样将请求封装成对象的我们称之为命令模式。所以命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。同时命令模式支持可撤销的操作。
命令模式可以将请求的发送者和接收者之间实现完全的解耦,发送者和接收者之间没有直接的联系,发送者只需要知道如何发送请求命令即可,其余的可以一概不管,甚至命令是否成功都无需关心。同时我们可以非常方便的增加新的命令,但是可能就是因为方便和对请求的封装就会导致系统中会存在过多的具体命令类。
参与者:
Command: 抽象命令类。用来声明执行操作的接口。
ConcreteCommand: 具体命令类。将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Excute。
Invoker: 调用者。要求该命令执行这个请求。
Receiver: 接收者。知道如何实施与执行一个请求相关的操作,任何类都有可能成为一个接收者。
Client:客户类。
3、解释器模式(Interpreter)
所谓解释器模式就是定义语言的文法,并且建立一个解释器来解释该语言中的句子。解释器模式描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发的编译器中。它描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。
参与者:
AbstractExpression: 抽象表达式。声明一个抽象的解释操作,该接口为抽象语法树中所有的节点共享。
TerminalExpression: 终结符表达式。实现与文法中的终结符相关的解释操作。实现抽象表达式中所要求的方法。文法中每一个终结符都有一个具体的终结表达式与之相对应。
NonterminalExpression: 非终结符表达式。为文法中的非终结符相关的解释操作。
Context: 环境类。包含解释器之外的一些全局信息。
Client: 客户类。
4、迭代器模式(Iterator)
对于迭代在编程过程中我们经常用到,能够游走于聚合内的每一个元素,同时还可以提供多种不同的遍历方式,这就是迭代器模式的设计动机。在我们实际的开发过程中,我们可能会需要根据不同的需求以不同的方式来遍历整个对象,但是我们又不希望在聚合对象的抽象接口中充斥着各种不同的遍历操作,于是我们就希望有某个东西能够以多种不同的方式来遍历一个聚合对象,这时迭代器模式出现了。
何为迭代器模式?所谓迭代器模式就是提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示。迭代器模式是将迭代元素的责任交给迭代器,而不是聚合对象,我们甚至在不需要知道该聚合对象的内部结构就可以实现该聚合对象的迭代。
通过迭代器模式,使得聚合对象的结构更加简单,它不需要关注它元素的遍历,只需要专注它应该专注的事情,这样就更加符合单一职责原则了。
参与者:
Iterator: 抽象迭代器:所有迭代器都需要实现的接口,提供了游走聚合对象元素之间的方法。
ConcreteIterator: 具体迭代器。利用这个具体的迭代器能够对具体的聚合对象进行遍历。每一个聚合对象都应该对应一个具体的迭代器。
Aggregate: 抽象聚合类。
ConcreteAggregate: 具体聚合类。实现creatorIterator()方法,返回该聚合对象的迭代器。
5、中介者模式(Mediator)
租房各位都有过的经历吧!在这个过程中中介结构扮演着很重要的角色,它在这里起到一个中间者的作用,给我们和房主互相传递信息。在外面软件的世界里同样需要这样一个中间者。在我们的系统中有时候会存在着对象与对象之间存在着很强、复杂的关联关系,如果让他们之间有直接的联系的话,必定会导致整个系统变得非常复杂,而且可扩展性很差!在前面我们就知道如果两个类之间没有不必彼此通信,我们就不应该让他们有直接的关联关系,如果实在是需要通信的话,我们可以通过第三者来转发他们的请求。同样,这里我们利用中介者来解决这个问题。
所谓中介者模式就是用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。在中介者模式中,中介对象用来封装对象之间的关系,各个对象可以不需要知道具体的信息通过中介者对象就可以实现相互通信。它减少了对象之间的互相关系,提供了系统可复用性,简化了系统的结构。
在中介者模式中,各个对象不需要互相知道了解,他们只需要知道中介者对象即可,但是中介者对象就必须要知道所有的对象和他们之间的关联关系,正是因为这样就导致了中介者对象的结构过于复杂,承担了过多的职责,同时它也是整个系统的核心所在,它有问题将会导致整个系统的问题。所以如果在系统的设计过程中如果出现“多对多”的复杂关系群时,千万别急着使用中介者模式,而是要仔细思考是不是您设计的系统存在问题。
参与者:
Mediator: 抽象中介者。定义了同事对象到中介者对象之间的接口。
ConcreteMediator: 具体中介者。实现抽象中介者的方法,它需要知道所有的具体同事类,同时需要从具体的同事类那里接收信息,并且向具体的同事类发送信息。
Colleague: 抽象同事类。
ConcreteColleague: 具体同事类。每个具体同事类都只需要知道自己的行为即可,但是他们都需要认识中介者。
6、备忘录模式(Memento)
后悔药人人都想要,但是事实却是残酷的,根本就没有后悔药可买,但是也不仅如此,在软件的世界里就有后悔药!备忘录模式就是一种后悔药,它给我们的软件提供后悔药的机制,通过它可以使系统恢复到某一特定的历史状态。
所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它实现了对信息的封装,使得客户不需要关心状态保存的细节。保存就要消耗资源,所以备忘录模式的缺点就在于消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
参与者:
Originator: 原发器。负责创建一个备忘录,用以记录当前对象的内部状态,通过也可以使用它来利用备忘录恢复内部状态。同时原发器还可以根据需要决定Memento存储Originator的那些内部状态。
Memento: 备忘录。用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。在备忘录Memento中有两个接口,其中Caretaker只能看到备忘录中的窄接口,它只能将备忘录传递给其他对象。Originator可以看到宽接口,允许它访问返回到先前状态的所有数据。
Caretaker: 负责人。负责保存好备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。
7、观察者模式(Observer)
何谓观察者模式?观察者模式定义了对象之间的一对多依赖关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。
在这里,发生改变的对象称之为观察目标,而被通知的对象称之为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。
所以观察者提供了一种对象设计,让主题和观察者之间以松耦合的方式结合。
参与者:
Subject:目标。他把所有对观察者对戏的引用保存在一个聚集里,每一个主题都可以有多个观察者。
Observer:观察者。为所有的具体观察者定义一个接口,在得到主题的通知时能够及时的更新自己。
ConcreteSubject:具体主题。将有关状态存入具体观察者对象。在具体主题发生改变时,给所有的观察者发出通知。
ConcreteObserver:具体观察者。实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态相协调。
8、状态模式(State)
在很多情况下我们对象的行为依赖于它的一个或者多个变化的属性,这些可变的属性我们称之为状态,也就是说行为依赖状态,即当该对象因为在外部的互动而导致他的状态发生变化,从而它的行为也会做出相应的变化。对于这种情况,我们是不能用行为来控制状态的变化,而应该站在状态的角度来思考行为,即是什么状态就要做出什么样的行为。这个就是状态模式。
所以状态模式就是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
在状态模式中我们可以减少大块的if…else语句,它是允许态转换逻辑与状态对象合成一体,但是减少if…else语句的代价就是会换来大量的类,所以状态模式势必会增加系统中类或者对象的个数。
同时状态模式是将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。但是这样就会导致系统的结构和实现都会比较复杂,如果使用不当就会导致程序的结构和代码混乱,不利于维护。
参与者:
Context:环境类。可以包括一些内部状态。
State: 抽象状态类。State定义了一个所有具体状态的共同接口,任何状态都实现这个相同的接口,这样一来,状态之间就可以互相转换了。
ConcreteState:具体状态类。具体状态类,用于处理来自Context的请求,每一个ConcreteState都提供了它对自己请求的实现,所以,当Context改变状态时行为也会跟着改变。
9、策略模式(Strategy)
我们知道一件事可能会有很多种方式来实现它,但是其中总有一种最高效的方式,在软件开发的世界里面同样如此,我们也有很多中方法来实现一个功能,但是我们需要一种简单、高效的方式来实现它,使得系统能够非常灵活,这就是策略模式。
所以策略模式就是定义了算法族,分别封装起来,让他们之前可以互相转换,此模式然该算法的变化独立于使用算法的客户。
在策略模式中它将这些解决问题的方法定义成一个算法群,每一个方法都对应着一个具体的算法,这里的一个算法我就称之为一个策略。虽然策略模式定义了算法,但是它并不提供算法的选择,即什么算法对于什么问题最合适这是策略模式所不关心的,所以对于策略的选择还是要客户端来做。客户必须要清楚的知道每个算法之间的区别和在什么时候什么地方使用什么策略是最合适的,这样就增加客户端的负担。
同时策略模式也非常完美的符合了“开闭原则”,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。但是一个策略对应一个类将会是系统产生很多的策略类。
参与者:
Context: 环境类。维护一个Strategy对象的引用,用一个ConcreteStrategy来配置,可定义一个接口来让Strategy访问它的数据。
Strategy: 抽象策略类。定义所有支持算法的公共接口。Context使用这个接口来调用某个Concretestrategy定义的算法。
ConcreteStrategy: 具体策略类。封装了具体的算法实现。
10、模板方法模式(Template Method)
有些时候我们做某几件事情的步骤都差不多,仅有那么一小点的不同,在软件开发的世界里同样如此,如果我们都将这些步骤都一一做的话,费时费力不讨好。所以我们可以将这些步骤分解、封装起来,然后利用继承的方式来继承即可,当然不同的可以自己重写实现嘛!这就是模板方法模式提供的解决方案。
所谓模板方法模式就是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
模板方法模式就是基于继承的代码复用技术的。在模板方法模式中,我们可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中。也就是说我们需要声明一个抽象的父类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法让子类来实现剩余的逻辑,不同的子类可以以不同的方式来实现这些逻辑。所以模板方法的模板其实就是一个普通的方法,只不过这个方法是将算法实现的步骤封装起来的。
参与者:
AbstractClass: 抽象类。实现了一个模板,实现算法的基本骨架,具体子类将重定义primitiveOperation()方法以实现一个算法步骤。
ConcreteClass: 具体子类。实现primitiveOperation()方法以完成算法中与特定子类相关的步骤。
11、访问者模式(Visitor)
访问者模式俗称23大设计模式中最难的一个。除了结构复杂外,理解也比较难。在我们软件开发中我们可能会对同一个对象有不同的处理,如果我们都做分别的处理,将会产生灾难性的错误。对于这种问题,访问者模式提供了比较好的解决方案。
访问者模式即表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式。同时我们还需要明确一点那就是访问者模式是适用于那些数据结构比较稳定的,因为他是将数据的操作与数据结构进行分离了,如果某个系统的数据结构相对稳定,但是操作算法易于变化的话,就比较适用适用访问者模式,因为访问者模式使得算法操作的增加变得比较简单了
参与者:
Vistor: 抽象访问者。为该对象结构中的ConcreteElement的每一个类声明的一个操作。
ConcreteVisitor: 具体访问者。实现Visitor申明的每一个操作,每一个操作实现算法的一部分。
Element: 抽象元素。定义一个Accept操作,它以一个访问者为参数。
ConcreteElement: 具体元素 。实现Accept操作。
ObjectStructure: 对象结构。能够枚举它的元素,可以提供一个高层的接口来允许访问者访问它的元素。
每个程序员书柜必备的编程书籍
一提到有关编程方面的书籍,你就会想起江湖中传说的“每个程序员必读的12本计算机科学书籍”,然而,这份清单并没有什么实用性。因为,计算机科学领域非常宽泛,几乎所有程序员都可以阅读任何主题,就算某个主题很重要,但由于读者的学习偏好天悬地隔,因此对所有读者而言,某个主题并不存在所谓“最好的”书籍。
本文是我已经阅读过的主题和书籍的清单。这些都是我很熟悉的主题,也许你可以在此文中,了解关于该主题更多的情况,并阅读其他相关书籍。换而言之,就是说你为什么需要阅读另一本书。
算法、数据结构、复杂性
为什么要关心算法、数据结构和复杂性? 好吧,有一个实用的结论:在工作中,即使你从来没有用到这些东西,但是,大多数一流的支付公司在面试中会测试这些内容。我决不瞎扯,算法真的很有用,就像我发现数学很有用一样。任何特定算法适用于任何特定问题的概率很低,但从总体上说明什么类型的问题如何解决,什么样的问题难以处理,通常当近似有效时就有用了。
《Cracking the Coding Interview》
作者:McDowell
出版社:CareerCup; 6th edition (July 1, 2015)
中文版:《程序员面试金典》
译者:李琳骁、漆犇
出版社:人民邮电出版社,第五版
《程序员面试金典》的作者McDowell,是原Google资深面试官的经验之作,层层紧扣了程序员面试的每一个环节,全面详尽介绍了程序员应当如何应对面试才能脱颖而出。
该书涉及到面试流程解析、面试官的幕后决策及可能提出的问题、面试前的准备工作、对面试结果的处理等内容,还涵盖了数据结构、概念与算法、知识类问题和附加面试题四个方面,为读者提供了来自Google、Facebook、Microsoft等多家知名公司的编程面试题,并为每一道面试题分别给出了详细的解决方案。
我会向那些并没有真正掌握算法、但又想通过面试的程序员推荐这本书。这本书读起来尚可,不过并不能真正教会你知识,如果想真正掌握算法和数据结构,那么继续往下看。
《Algorithms》
作者:Dasgupta、Papadimitriou、Vazirani
出版社:McGraw-Hill Education
中文版:《算法概论》
译者: 王沛、唐扬斌、刘齐军
出版社:清华大学出版社
《算法概论》是一本适合入门的读物,但却不失深度以及广度,读来让人兴趣盎然。我认为,认真读完这本书,并且思考每章后面的习题,会对算法有一个很好的大局观。当然要掌握算法,只靠这一本书是不够的,不过算作最佳入门是当之无愧的。
该书全面介绍了算法的基本知识,将算法分成类(例如分治法、,或贪婪算法),在表达每一种技术时,强调每个算法背后的数学思想,让你掌握如何判断应该使用什么样的算法来解决特定的问题。
该书合理挑选主题,厘清了一条算法这门学问的线索,对结构的精心安排,对问题的数学结构的剖析,从而推出一个算法的过程的讲解。长度适宜,仅有三百多页,可以利用几个周末进行阅读。
不过,我知道有些读者不会喜欢这本书,因为它包括了太多的数学思想。如果是你,我猜你可能喜欢Skiena著的那本书。
《The Algorithm Design Manual》
作者:Skiena
出版社:Springer; 2nd edition (July 26, 2008)
《The Algorithm Design Manual》要比Dasgupta所著的那本《Algorithms》更长,更全面,也更实用,而且涉及到的数学也更少。它差不多就是试图教你如何识别问题并使用正确的算法来解决问题、以及给出一个清晰的算法解释的那本书。
该书内容丰富,各个主题几乎无不涉及,从常见的各种数据结构到算法策略、可计算性。如果想当做教科书,该书每章结尾都有很多练习,值得推荐。这本书充满了激情的“战争故事”,显示了算法在现实世界编程的影响。
《Introduction to Algorithms》
作者:Cormen、Leiserson、Rivest、Stein(CLRS)
出版社:The MIT Press; 3rd edition (July 31, 2009)
中文版:《算法导论》
译者:殷建平、徐云、王刚、刘晓光、苏明、邹恒明、王宏志
出版社:机械工业出版社; 第1版 (2013年1月1日)
在江湖中流传的各种版本“所有程序员必读之书”的清单中,《算法导论》就占据了半壁江山。这本书将严谨性和全面性融为一体,深入讨论了各种算法。实际上,几乎没有读者会读完整本书。不过,全书各章自成体系,可以作为独立的学习单元。它是全球读者最多的算法圣经。
该书的特点是选材经典、内容翔实、结构合理、逻辑清晰。每章前半部分介绍了讲授和学习算法的有效方法,后半部分为更专业的读者和求知欲强的学生提供了更引人入胜的资料来讨论这个迷人领域的各种可能性和挑战,对本科生的数据结构课程和研究生的算法课程而言是非常棒的教科书。
比如说,有整整一章是关于Van Emde Boas树,写得很工整,这有点奇怪,像的平衡树结构的插入、删除、就像查找、继承一样好,也许以前的书籍可能如此,但它是第一个没有包含Van Emde Boas树的算法导论。
《Algorithm Design》
作者:Kleinberg、Tardos
出版社:PEARSON EDUCATION; 1 edition (2013)
《Algorithm Design》广受好评,堪比CLRS所著《Introduction to Algorithms》,被推荐为关于算法设计和分析的一本不可多得的优秀入门书。我发现,这本书与CLRS所著的书相比,该书将直观性与严谨性完美结合起来。覆盖面比较宽广,凡属串行算法的经典论题,都有所涉猎。
《Advanced Data Structures》
作者:Demaine
《Advanced Data Structures》是一套讲稿和笔记,而非一本书。如果你想要一套条理清楚但不难综合的数据结构的学习资料,在大多本科课程中,你不大可能看到,因此这套讲课和笔记,功德无量。
这些笔记没有出单行本的计划,所以如果您尚未看过这些资料,就必须观看教学视频。
《Purely Functional Data Structures》
作者:Okasaki
Cambridge University Press (June 13, 1999)
出版社: Cambridge University Press
《Purely Functional Data Structures》读起来很有趣,但不同于其他算法和数据结构的读物的是,我从这本书中,还没能得到真正解决实际问题的重要能力。
在我读完这本书后过了几年,有人告诉我,推理出纯函数冗余数据结构的性能并不难。我就向他们请教这本书中把我难倒的证明部分,我并不是在讨论那些晦涩难懂的超难习题,而是说这本书的主要内容,作者有太多过于明显的解释性的东西。但是并没有人解释。这种东西的推理比大家说的还要难。
《Higher Order Perl》
作者:Dominus
出版社: Morgan Kaufmann Publishers
中文版:《华章程序员书库:高阶Perl》
译者:滕家海
出版社:机械工业出版社
《高阶Perl》在Perl领域深入探讨了各种最新的主题,如递归、迭代器、过滤器、划分、数值方法、高阶函数、惰性求值等内容,并将这些转换成现实变成工作中强有力的实用工具:HTML处理、数据库访问、网页抓取、家庭理财等等。
该书恰当地介绍了使用Perl的函数式编程。通过这本书的学习,你可能像Python或Ruby那样很容易进行工作。
不过,如果你跟得上潮流,你会发现这本书可能有点过时,因为讲解的是2005年的Perl 5,而现在是2015年12月25日发行的Perl 6。不过,这本书提到的思想,目前已成主流。
《Algorithms》
作者:Levitin
出版社:Pearson; 3 edition (October 9, 2011)
“强化学习的其他特点,包括:章节摘要,习题提示。这是一本详细的解决方案手册。”、“学生通过习题提示和章节摘要进一步支持学习。”其中一个广告甚至印在书上。
当我看到这两个广告后,就在亚马逊下了订单购买《Algorithms》。但在我拿到这本书后,我唯一的自学资源却是在雅虎问答(Yahoo Answers)里的一些帖子,在那里你能找到提示或者解答。
最后,我拿起了Dasgupta的著作来取代了这本书,因为他的网站资源可以免费使用。
《Probability and Computing: Randomized Algorithms and Probabilistic Analysis》
作者:Mitzenmacher、Upfal
出版社: Cambridge University Press (January 31, 2005)
我获得的算法知识可能比其他任何算法书籍讲述的都多。许多随机算法移植到其他应用程序虽然很琐碎,但却可以简化很多事情。
《Probability and Computing: Randomized Algorithms and Probabilistic Analysis》有关概率学的介绍部分很翔实,就算读者不具备任何概率学的背景也能轻松入门。此外,的内容(例如,)对许多计算机科学理论的证明很有用,这些在我所阅读到的概率学介绍内容中都没有提到。
《Introduction to the Theory of Computation》
作者:Sipser
出版社:Cengage Learning; 3 edition (June 27, 2012)
中文版:《计算理论导引》
译者:段磊、唐常杰
出版社:机械工业出版社,第一版
《计算理论导引》以独特的视角,系统介绍了计算理论的三大板块:自动机与语言、可计算性理论、计算复杂性理论。讲述了宽泛的数学原理,没有拘泥于某些低层次的细节。
在证明之前,都有直观的“证明思路”,帮助读者理解数学形式下蕴涵的概念。很多重要的结果,如定理,都被作为练习题。所以你真的需要做这些关键练习题。但是,大多数关键练习题没有提供答案,因此你也无法知道你解答是否正确。
如果想选择更为现代的主题,也许可以阅读Aurora和Barak的著作。
《Computation》
作者:Bernhardt
《Computation》这部教学影片涵盖了一些计算机理论的重点部分。影片讲解令人愉悦,为了观看Bernhardt讲解的内容,这个影片我已经观看不止一次了。该影片的受众是那些没有计算机科学背景的一般程序员。
《An Introduction to Computational Learning Theory》
作者:Kearns & Vazirani
出版社:The MIT Press (August 15, 1994)
《An Introduction to Computational Learning Theory》是一本非常经典的读物。但是这本书已经过时(InfoQ注:该书1994年出版,距今已经22年了),而且漏洞百出,没有勘误。我最终从几门课程拼凑了一些笔记,一个是Klivans的著作,另一个是Blum的著作。
操作系统
为什么要关心操作系统?因为,掌握一些关于操作系统的知识,可以让你节省几天或一周的调试时间。 这是Julia Evans博客上经常提到的话题,我发现,就我个人经验来说,确实如此。
那些建立可行的系统并了解一些操作系统知识的人,却没有发觉如果有操作系统知识的话会很节省时间,这点我很难想象。
可是,读过操作系统书籍的人往往有偏见——正是做这方面的相关人士,如果你是骨灰级玩家,除非阅读这些,你可能得不到同样的结果。
《Operating System Concepts》
作者:Silberchatz、Galvin、Gagne
出版社:Wiley; 9 edition (December 17, 2012)
中文版:《操作系统概念》(第七版)(翻译版)
译者:郑扣根
出版社:高等教育出版社; 第1版 (2010年1月1日)
好吧,这是Comet Book成为标准之前,我们在Wisconsin就用过的一本书。
《操作系统概念》涵盖了高阶概念并击中要点,但是在技术层次的深度稍显不足,没有详细阐述事情是如何工作的,也没有清晰列出更高级的主题。
顺便说一下,我听说了关于Comet书籍的好消息,但是我不敢妄言,毕竟我还没有阅读过。
《xv6》
作者:Cox、Kasshoek、Morris
这本《xv6》真的太棒了!它解释了你如何能够在真实系统上有效运作,你可以设想自己实现一个可执行的操作系统。按照本书写作的设计,作者倾向于简单的实现而非优化的实现,因此,书中使用的算法、数据结构和平常的生产系统完全不同。
这本书与介绍现代操作系统如何工作的书籍配合阅读,比如Love著的《Linux Kernel Development》或者Russinovich著的《Windows Internals》,学习效果会更好。
《Linux Kernel Development》
作者:Love
出版社:Addison-Wesley Professional; 3 edition (July 2, 2010)
《Linux Kernel Development》的书名可能有误导之嫌,这不是关于Linux内核开发的读物,基本上是一本阐述Linux内核如何工作的书籍:事物是如何契合的,使用了什么算法和数据结构等等。我阅读的是第二版,现在已经完全过时了。
第三版内容有所更新,但是也引进了一些错误和矛盾之处。而且,这一版本仍然过时,这本书2010年出版,讲的是内核2.6.34。虽然如此,该书仍然不啻一本优秀的介绍现代操作系统原理的读物。
该书还有一个缺点,在作者拿Linux和Windows进行比较时,有失客观,基本上就是每次比较的时候,就宣扬Linux是明确无疑的正确选择,Windows总是干蠢事。总体来说,Linux和Windows我都喜欢,在有些领域,Windows做得更好。而且在有些地区,Windows已经领先很多年了。但在这本书中,你甭想看到类似这些评价。
《Windows Internals》
作者:Russinovich、Solomon、Ionescu
出版社:Microsoft Press; 6 edition (March 25, 2012)
中文版:《深入解析Windows操作系统:第6版(上册)》
译者:潘爱民、范德成
出版社:出版社: 电子工业出版社; 第1版 (2014年4月1日)
《深入解析Windows操作系统》是阐述现代操作系统如何工作最全面的一本书,只不过碰巧这本书是关于微软Windows。作为从nix走过来的人,看到Windows和nix不同的差异,这样的阅读非常有趣。
然而,该书并非简单的入门书,在阅读本书之前你必须掌握一些操作系统知识。
如果想买这本书,你要等到2017年初发行的第七版。
《The Little Book of Semaphores》
作者:Downey
《The Little Book of Semaphores》是一本这样的读物:列出一个主题,通常从操作系统的教科书中抽取一两个章节,然后拼凑成为自己长达300页的书。
这本书是一系列的习题,有点像小型提纲,但更多的是阐述。它首先解释了什么是信号量,然后给出构建高并发原语知识的一系列习题。
在我开始编写并发线程代码时,这本书提供了很大的帮助。我订阅了Butler Lampson学院关于并发的资料,我喜欢把并发相关代码塞到别人写的黑盒。但是有时候你坚持自己写黑盒的话,如果是这样,这本书有很好的导论,要求思考方式才可能写出不是完全错误的并发代码。
我希望有朝一日,能有这样的一本书问世:既照顾低水平的读者,又兼顾高水平读者,我很喜欢这样的写作设计。从几个指令集原语不同的内存模型的体系结构(如x86和Alpha),而不是从信号量开始讲起。如果今天我写低劣的低级线程代码,我会更喜欢用C++ 11的线程原语,所以我想用这些东西而非信号量。如果由我来写线程代码的话,我可能会用Win32 API来编写。但到目前为止,还不存在这样的一本书。以后如果有这样一本书问世,那将是最好不过的了。
我听说Doug Lee的Java并发编程非常不错,但我只泛泛而读没有深入研读。
计算机体系架构
为什么要关心计算机体系架构?当你进行底层性能优化的时候,你所学到的具体事实和细节,将会非常有用。但是,真正的价值就是学习如何在性能和其他因素进行权衡,无论是功耗、成本、体积、重量,亦或者其他因素。
从理论上讲,这种推理应该不分专业进行教授,但我的经验是,那些学习计算机体系结构的人更可能会“得出”那种推理和粗略的计算:才能让他们抛开一个没有理由在性能上2倍或10倍(或100倍)的因素。听上去很显然,但是我想到大公司的多个生产系统放弃10到100倍的性能,而以一个标准来运行,甚至2倍的性能差异,都足以支付VP的薪水。全是因为人们没有意识到他们的设计带来的性能影响。
《Computer Architecture: A Quantitative Approach》
作者:Hennessy、Patterson
《Computer Architecture: A Quantitative Approach》教你如何用多约束(如性能、TCO(总开销)、功率等)进行系统设计,以及如何推论权衡。它主要以微处理器和超级计算机为例。
该书的新版增加了实质性的补充,这个版本才是你真正想要的读物。比如,新版增加了一个关于数据中心设计的章节,回答了以下问题:用于电力、功率分布、制冷的运营开支OPEX/资本支出CAPEX,以及支持团队和机器的支付费用,使用低功耗机器对tail larency的结果质量及影响(以必应搜索的结果为例),在设计数据中心时应该考虑哪些其他因素。
假设读者具备一些背景,但背景已在附录中提供,并且可免费在线获取。
《Modern Processor Design》
作者:Shen、Lipasti
出版社:Waveland Press, Inc.; 1 edition (July 30, 2013)
《Modern Processor Design》展示了设计Pentium Pro(1995年)时代高性能微处理器所需要了解的大部分内容。因为这种处理器的复杂性,阐述清楚绝非易事。另外,还介绍了一些更为先进的想法和从各种工作负荷可以运行的平行计算的范围(以及你可能会如何进行如此计算)。该书有一个很长的章节,是关于值预测,因为作者发明了这个概念,当第一版发行的时候,还很热门。
对于纯CPU架构,这本可得到的读物大概是最好的。
《Readings in Computer Architecture》
作者:Hill、Jouppi、Sohi
出版社:Morgan Kaufmann; 1 edition (September 23, 1999)
因为历史原因而阅读,看看我们所理解的解释有多好。比如,比较Amdal关于定律的论文(没有公式,只有一幅并非显而易见的图表的两张纸),相对与现代教科书的表述(一个段落,一个公式,或者一幅图表来阐明,虽然通常来说足够清晰,不需要额外的图表)。
糟糕的是,《Readings in Computer Architecture]》看上去让你后退得更远。因为计算机架构真的是很年轻的领域,这里没有什么真正难以理解的东西。如果你想看到一个动人心魄的例子,例如我们怎样更好解释这一些,像比较Maxwell在方程组的最初论文与现代对同一事物的解释。如果你喜欢历史的话,这本书很有趣。但是如果你只是想学习的话,它勉为其难。
博弈算法理论、拍卖理论、机制设计
为什么要关注这些知识(博弈算法理论、拍卖理论、机制设计)?因为这些知识介绍了世界上最大的科技公司在广告收入的运作方式,而这些广告是通过拍卖来销售的。该领域介绍了它们运作的方法和理由。另外,当你尝试弄明白如何设计有效分配资源的系统,这些知识就派上用场了。此外,如果你玩桌游,拍卖理论解释了为什么通过拍卖机制来固化博弈失衡是重要的,往往使游戏更糟。
尤其对设计公司内部激励相容制度(粗略的说,就是如何创建能提供人们出于私心而追求最佳利益的全局最优结果的系统)的人员而言,这些书应为必读书籍。如果你曾在两家大公司供职过,其中一家建立了有效的内部激励相容制度,而另一家没有建立相应制度。你就会发现,没有建立内部激励相容制度的那家大公司烧了大把大把的钱,因为这些人建立了超级没用的激励系统。
这个领域展现了这么一幅画卷,让你了解什么样的机制会带来什么样的结果。通过阅读案例研究,你会得到一个耗资数百万甚至数十亿美元的错误列表,洋洋洒洒,也很有趣。
《Auction Theory》
作者:Krishna
出版社:Academic Press; 2 edition (August 26, 2009)
中文版:《拍卖理论》
译者:罗德明、奚锡灿
出版社:中国人民大学出版社
上一次我阅读《拍卖理论》的时候,它是当时唯一对拍卖理论进行全面且最近介绍的一本书。在第一章中,涵盖了经典的第二价格拍卖的结论,然后涵盖了风险规避、竞标操纵、多个拍卖、非对称信息和其他现实世界的问题。
该书涵盖了拍卖的大部分理论,对深入理解拍卖理论的主要脉络非常必要。但这本书相当枯燥无趣,不大可能激起阅读欲望。除非你对这个主题感兴趣。需要了解基本的概率学和微积分学的知识。该书对致力拍卖研究的研究生是不可或缺的读物。
《Snipers, Shills, and Sharks: eBay and Human Behavior》
作者:Steighlitz
出版社: Princeton University Press (April 1, 2007)
《Snipers, Shills, and Sharks: eBay and Human Behavior》看上去似乎是专门为外行介绍拍卖理论、带有娱乐性质的书籍。该书解释了eBuy的工作方式及理由,正如书名的副标题所言,作者还探讨了人类在eBuy和其他地方拍卖中的怪癖行为。这本书妙趣横生,无需读者具备数学背景,可能包含了Krishna所著《Auction Theory》的少量内容,能让读者对拍卖理论产生兴趣。
《Combinatorial Auctions》
作者:Crampton、Shoham、Steinberg
出版社: The MIT Press
《Combinatorial Auctions》所讨论的是,是像美国通信委员会(FCC)那次频段拍卖,由于拍卖机制设计中的“漏洞”引起数亿甚至数十亿美元的价差被摆上台面的案例(InfoQ注:有兴趣的读者可以通过google检索“美国拍卖600MHz频段”来了解这个事件)。该书虽然是由不同作者共同撰写的读物,但读起来仍然行云流水一气呵成,让我乐意阅读。
这本书在组合拍卖领域有着深度和广度,于细微处见功夫,它还罗列了详细的作者和主题索引,排版质量非常棒,以至于可以跳过开头提到的Krishna的著作进行阅读,但我不会推荐它。这本书对于想了解组合拍卖的研究人员和从业者,都是必不可少的读物。
《Multiagent Systems:Algorithmic,Game-Theoretic,and Logical Foundations》
作者:Shoham、Leyton-Brown
出版社:Cambridge University Press; 1 edition (December 15, 2008)
《Multiagent Systems:Algorithmic,Game-Theoretic,and Logical Foundations》最槽的就是书名了。然而,它是Multiagent System方面最值得阅读的读物之一。
该书涵盖了基本的博弈论、拍卖理论,以及在计算机科学中读者可能不知道的其他经典主题,囊括了很多新颖的前沿理论,还有计算机科学和这些主题的交集内容,比如博弈学习模型等,甚至还有逻辑学的内容(这也是Shoham最关注的方向,将博弈理论上升到哲学层次)。这本书非常有特色,前面几章有很多例子,计算机科学学术味很浓。该书假定读者对主题没有特定的背景。
《Algorithmic Game Theory》
作者:Nisan、Roughgarden、Tardos、Vazirani
出版社:Cambridge University Press; 1 edition (September 24, 2007)
《Algorithmic Game Theory》包含了博弈算法理论的各种调查结果。要求读者具有相当数量的背景(首先要阅读过Shoham和Leyton-Brown的著作),例如第五章基本上是Devanur、Papadimitriou、Saberi和Vazirani的JACM论文:
《Market Equilibrium via a Primal-Dual Algorithm for a Convex Program》(《通过用于Convex程序的原始对偶算法的市场均衡》),并引出一些相关问题,让读者有更上一层楼的动机。如果你深入了这本书的话,会发现该书阐述很棒,而且很有趣。但如果你想通过阅读一本书来窥探一个领域的话,它未必是你想要的那种书。
杂项
《Site Reliability Engineering》
作者:Beyer、Jones、Petoff、Murphy
出版社:O’Reilly Media; 1 edition (April 16, 2016)
中文版:《SRE:Google运维解密》
译者:孙宇聪
出版社:电子工业出版社; 第1版 (2016年10月1日)
《SRE:Google运维解密》可以让读者学习到Google工程师在提高系统部署规模、改进可靠性和资源利用效率方面的指导思想与具体实践——这些都是可以立即直接应用的宝贵经验。任何一个想要创建、扩展大规模集成系统的人都应该阅读,该书针对如何构建一个可长期维护的系统提供了非常宝贵的实践经验。
要看更丰富的简要说明,请阅读这本22页的该书的笔记。
《Refactoring》
作者:Fowler、Beck、Brant、Opdyke、Roberts
出版社:Addison-Wesley Professional; 1 edition (July 8, 1999)
中文版:《重构:改善既有代码的设计》
译者:熊节
出版社:人民邮电出版社; 第2版 (2015年8月1日)
那时候当我读这本《重构:改善既有代码的设计》时,由于它在关于代码异味的单独章节所花的篇幅,使得这本书非常值这个价。该书非常成功,因为它使重构和代码异味的观念成为主流。
Steve Yegge曾对这本书不吝赞誉之词:
2003年10月,我第一次阅读这本书,有一种不寒而粟的感觉。如果你意识到,当你想离职时,你已经工作5年。转天我就随意问周围:“嗯,你已经读过重构方面的书,对吧?我只是随便问问,因为我很久以前读过,当然不是现在。”在我所调查的20个人中,只有一个人读过。感谢上帝,所有的人都很尴尬,不只是我。
……
如果你是资深工程师,你会发现该书中至少80%,都是你所熟知的东西。但该书罗列了所有的名称,并客观地讨论这些技术的利弊,这一点很有用。它戳穿了我刚成为程序员时所珍藏的若干个“秘籍”的真相。不注释代码?局部变量是万恶之源?这疯子是狂人吗?要不要阅读这本书,取决于你自己!
《Peopleware》
作者:Demarco、Lister
出版社:Addison-Wesley Professional; 3 edition (June 28, 2013)
中文版:《人件(原书第3版)(珍藏版)》
译者:肖然、张逸、滕云
出版社:机械工业出版社; 第1版 (2014年9月1日)
在大学时我读到这本《人件》,该书似乎很令人信服,甚至所有的研究结果都支持该书的观点:不设置截止日期就比设置截止日期要好;办公室比隔间要好,等等。所有开发人员跟我讨论的时候,基本都赞同这些观点。
但实际上每家成功的公司都是以截然不同的方式运作的。甚至微软公司也从个人办公室转为开放式办公室进行了建筑改造。是不是这些观点都无所谓?如果真的很重要的话,那些公司怎么会成为真正信徒,像Fog Creek公司不去践踏竞争对手?
因为该书符合我的“偏见”,我就希望这本书是正确的。但是,荟萃分析(InfoQ注:Meta分析,指将多个研究结果整合在一起的统计方法)的理念让我用鉴定的眼光重读来查证主要来源。
《Renegades of the Empire》
作者:Drummond
出版社:Crown; 1 edition (November 16, 1999)
《Renegades of the Empire》讲述了DirectX历程的故事。它还揭示了今天的微软狼性文化是怎么形成的。阅读介绍:
微软未必会雇佣像盖茨的人(虽然企业园区有不少这样的人),但会招聘那些有着更为显著的盖茨特征的人——傲慢、进取心和高智商。
……
因为嘲笑某人的想法是“愚蠢的”,或者更为槽糕的是,“胡说八道”——盖茨因此臭名昭著——只是为了看他如何捍卫一个位置。这种敌对的管理技术贯穿一系列的命令,创造了一种冲突的文化。
……
微软建立了达尔文的秩序,资源经常被掠夺,囤积力量、财富和威望。一名外出休假的经理回来时可能会发现他的部门被竞争对手袭击,他的项目被勒令降级或者完全取缔。
在微软面试:
“你喜欢微软什么呢?”“比尔踢屁股。”St. John说,“我喜欢踢屁股。我喜欢那种扼杀竞争对手和支配市场的感觉。”
结果St. John被雇佣了。多年来他没有犯任何错误。这本书就讲述了他的故事,以及像他那样的员工的故事。如果你想在微软公司谋差,你就需要读这本书。我希望我加入之前就读过这本书而不是之后!
数学
为什么要关注数学?从纯ROI(InfoQ注:Return On Investment,投资回报率)观点来看,我怀疑,对于99%的工作,学习数学是值得的。据我所知,我用数学比大多数程序员要多,但我并没有那么经常使用数学。不过,有正确的数学背景可能会派上用场。我很享受学习数学的乐趣。
《Introduction to Probability》
作者:Bertsekas
出版社:Athena Scientific; 2nd edition (July 15, 2008)
中文版:《概率导论(第2版)》(修订版)
译者:郑忠国、童行伟
出版社:人民邮电出版社; 第1版 (2016年1月1日)
《概率导论》是入门的大学课程,对 阐述定义比较严谨、直观。对任何关心严密推导的读者而言,该书后面有一些更为详细的习题。有很多可用解决方案的练习题,使得本书更适宜作为自学用书。
《A First Course in Probability》
作者:Ross
出版社:Pearson Prentice Hall; 8th edition (January 7, 2009)
中文版:《华章教育·华章数学译丛:概率论基础教程(原书第9版) 》
译者:童行伟、梁宝生
出版社:机械工业出版社; 第1版 (2014年1月1日)
为了使学生购买《概率论基础教程》,该书经常推出新版本,亚马逊定价更是高达174美元,我曾跟作者请教过这个问题,他抱怨说,这几年,二手书市场破坏了教科书的收入,而作者的版税并不多,所以要有更多收入,就不得不保持每两年推出新版的节奏来保证版税收入。
新作者要编写前人著过的经典书籍,经常大发牢骚,因为最早的作者通常比后来的作者拿更多的版税,即使他的后续版本并没有什么更新。
我在Wisconsin学习概率学的时候,该书是一本标准的教科书。我真的想不起有谁发现这本书有所帮助。
《Introductory Combinatorics》
作者:Brualdi
出版社:Pearson; 5 edition (January 7, 2009)
中文版:《组合数学(原书第5版) 》
译者:冯速
出版社:机械工业出版社; 第1版 (2012年5月1日)
Brualdi是一名大教授,是我大学生涯最好的教授之一。但是他著的《组合数学》充满了错误,而且也不特别清晰。自从我使用该书后,有两个新版本,但从亚马逊评论来看,这本书仍然有很多错误。
至于另一本基础入门型的教科书,我听过关于Camina和Lewis合著的《An Introduction to Enumeration (Springer Undergraduate Mathematics Series)》好消息,但我自己没有读过。此外,Lovasz的《Combinatorial Problems and Exercises (AMS Chelsea Publishing)》是一本关于组合数学的名著,但它并未被广为人知。
《Calculus》
作者:Apostol
出版社:Wiley; 2nd edition (January 16, 1991)
《Calculus》第一卷涵盖了你所期望的微积分I和微积分II的内容。第二卷涵盖了线性代数和多元微积分。在多元微积分之前,讲述线性代数,这样使得多元微积分更易理解。
从编程角度来看,微积分学也是很有意义的。因为我在微积分得到的价值观就是近似应用等等。教授这一连串的概念,很清晰。
如果你没有教授或助教帮你的话,该书可能是一本粗略的入门书。Spring SUMS系列丛书在各门功课上更易自学,但我并没有读过它们的微积分书籍,因此,我不敢妄下结论来推荐。
《Calculus》
作者:Stewart
出版社:Cengage Learning; 7 edition (2012)
中文版:《微积分(第六版)》(双语教材)
译者:张乃岳
出版社:中国人民大学出版社; 第1版 (2014年10月1日)
《微积分》是那些作者通过无关紧要的变更推出新版来赚钱的众多书籍中的一本。这是Wisconsin大学Non-Honor学位的标准教科书。结果是,我教了很多人用Apostol的那本书所教授的方法来计算复杂的微积分,对许多人而言,更直观一些。
该书采用的方式是,对于某种类型的问题,你应该将该模式匹配很多可能的公式,然后套用该公式。而Apostle更多的是教授你一些技巧和直觉,让你足以应付各种各样的问题。我不知道你为什么会买这本书,除非你需要一些类。
硬件基础
为什么要关注硬件基础?人们经常宣称,要成为优秀程序员,你必须理解所用的每一个抽象概念。这是无稽之谈,因为现代计算过于复杂,以至于任何人都不可能真正完全理解到底发生了什么事情。事实上,现代计算之所以能高效完成它所做的工作,是因为它不需要程序员深入了解底层的相关内容,大部分也低于你的水平。
话虽如此,如果你对底层软件充满好奇,这里有一些适合你的入门书籍。
《nand2tetris》
作者:Nisan、Shocken
如果你只想读一本单一内容的书,那么非这本《nand2tetris》莫属。它是一本关于逻辑门和布尔逻辑的“101级”入门书。正如书名所示,它带你从与非门(NAND gate)到编一个可用的俄罗斯方块游戏。
《Fundamentals of Logic Design》
作者:Roth
出版社:CL Engineering; 5 edition (June 11, 2003)
《Fundamentals of Logic Design》在关于逻辑门和逻辑设计的细节上比《nand2tetris》还要多。该书有大量习题,似乎是为自学而著。注意,上面的链接是第五版,目前有更贵的新版本,但似乎没有什么改进,而且新版也有很多错误,而且更昂贵。
《CMOS VLSI Design》
作者:Weste、Harris、Bannerjee
出版社:Pearson; 4 edition (March 11, 2010)
中文版:《CMOS超大规模集成电路设计(第4版) 》
译者:周润德
出版社:电子工业出版社; 第1版 (2012年7月1日)
逻辑门下一级的就是VLSI(very large scale integration),即超大规模集成电路。然而,在今天真的没有任何意义。
《CMOS超大规模集成电路设计》比其他书籍更有广度和深度,并且阐述极为清晰。在探索设计空间,比如,加法器的章节,不仅仅提及了几种不同的类型,而且也提供了问题和解决方案,非常适合自学。
《CMOS Digital Integrated Circuits》
作者:Kang、Leblebici
《CMOS Digital Integrated Circuits》是Wisconsin当前的教科书。但这本书很难跟上,助教基本上重新解释了几乎所有的必要项目和考试。我觉得它是参考书而不是用来学习的读物。
与West等人相比,Weste 花费了更多的精力讨论设计中的折衷,如,创建并行前缀树加法器时,在设计空间的某个特定点,它意味着什么?
《Semiconductor Device Fundamentals》
作者:Pierret
出版社:Addison Wesley; 2nd edition (April 12, 1996)
超大规模集成电路(VLSI)下一级,也就是晶体管,你将了解晶体管实际上是如何工作的。
《Semiconductor Device Fundamentals》真正完美地解说了固态设备(SSD)。该书指出了你要真正理解诸如波段图解的这些东西,需要知道什么。然后用这些基础原理和清晰的解释,给你建立一个良好的思想模型,理解不同类型的交汇点和设备的工作原理。
《Pentium Pro and Pentium II System Architecture》
作者:Shanley
出版社:Addison-Wesley Professional; 2 edition (January 10, 1998)
与本文提到的其他书不同,《Pentium Pro and Pentium II System Architecture》是关于实践而非理论。它有点像Windows内部,因为它涉及一个真实的工作系统的细节。主题包括硬件总线协议、I/O实际上是如何工作的(如APIC,Advanced Programmable Interrupt Controller,即高级可编程终端控制器)等等。
实际介绍的问题,就是从8080的CPU以来,复杂性一直呈指数级的增长。当你学得越深,你就越容易理解系统重要的可移动部分,而知识越不相关。因为总线和I/O协议不得不操作多处理器,这本书似乎妥协了,这些系统包含了现代系统中的许多元素,只不过是以更简单的形式。
未尽事宜
在这些我喜欢的读物中,我会说,这些图书中,软件读物最多占据25%、硬件读物占据5%。一般说来,未在清单中罗列出来的读物更为专业。清单还缺少很多领域的主题,如PL,关于如何学习编程语言的实用书籍、网络等等。
未涉及某些领域的原因有多种,比如我没有列出任何PL相关书籍,因为我不阅读PL方面的书籍。我没有提到网络是因为我虽然读过一些书,但我这方面的了解程度不足以提供有用书籍的建议。
绝大部分硬件相关的书籍都没有包含在内,因为它们涉及到你不会关心的内容,除非你是专家(比如容错电路设计(《Skew-Tolerant Circuit Design (The Morgan Kaufmann Series in Computer Architecture and Design》,Harris著)或超快光学《Ultrafast Optics》,Weiner著))同样也适用于数学和计算机科学理论。
我遗落了相当数量的一些我认为是名著的读物,因为在我日常编程生活中基本没有机会用到,比如极值组合论(Extremal Combinatorics)。我也没有罗列那些我没有读完的书籍,除非我停下来,因为这些书籍极为晦涩难读。因为我没有读完像SICP和The Little Schemer的书籍,这意味着我无法列出经典的清单。那些书籍很好,只是我没有完成阅读的原因。
清单中还不包括历史和文化相关的书籍,像《Inside Intel》或《Masters of Doom》。我可能会在未来某个时候,在清单中添加一个类别,就是我一直尝试的实验,像Julia Evans(意识流,文字更少或者没有草稿)那样撰写。
我必须回去重读十多年前曾经阅读过的书,然后写出有意义的评论,但这不符合我这个实验。关于这一点,因为这份清单是根据我记忆写的,几年前我就几乎没再读过那些所有的书,而且可能有所遗忘很多书,我打算日后将补充。
Github Hacking
我们都听过社会工程学这个名词,准确来说,社会工程学不是一门科学,而是一门艺术。而在社会工程学中,对信息掌握的充分程度是其中的关键。不光是这,在其它很多领域里我们都需要尽可能的搜集多的信息。
为了搜集更多的信息,目前主流的搜索途径有:
1.Google Hacking,这个就不必多说了,google强大的搜索引擎,只要会用,搜出来的真是超出意料。(比如下面这个搜出来的名单,包括身份证,手机,qq等信息应有尽有)
2.Shodan,物联网搜索引擎,是互联网上最可怕的搜索引擎,搜索所有和互联网关联的服务器、摄像头、打印机、路由器等等。
3.ZoomEye,是知道创宇推出的一款网络空间搜索引擎,探索互联网设备和网站节点,包括路由器、物联网家电、平板电脑、手机等,甚至还有监控探头、工业控制中的SCADA系统等比较敏感的设备,可以说是国版Shodan。
4.社工库,反正就是各种脱库流出的数据,只能说数据泄漏无处不在。。
上面都是一些废话,与本文无关。上面都是在不同层面上对信息的一种检索,而本文则提供另一种思路,我们从另一个维度进行探索——代码搜索引擎。
Github Hacking
Github不仅能托管代码,还能对代码进行搜索,我们感受到了其便利的同时,也应该时刻注意,当你上传并公开你的代码时,一时大意,让某些敏感的配置信息文件等暴露于众。
是不是还挺欢乐的,早就有无数人登陆过了,还有人留下文本善意提醒。这意味着什么,Github早已被盯上,也许下一个大事件会是某漏洞导致Github私有库代码大量泄漏。
当我们在Github上搜索时,我们到底能搜到什么
能搜到的东西很多,这里只是给个思路,具体怎么玩自己去尝试。
邮箱
比如说以mail password
关键字搜索:
搜索很多邮箱的帐号密码,这里就不一一列举了。
如果说用code>@qq.com或者是code>@gmail.com等各种邮箱后缀为关键字进行搜索,你会发现某商户收集的客户qq信息:
各种账号密码
Github上能搜到的账号密码实在是太多了,筛选一下你会发现很多有意思的。比如说有微信公众平台帐号:
居然连Github的登陆帐号也放在上面。。
各种VIP
万万没想到啊,没想到Github上还有这等福利!
百度云
尽管大部分链接已经失效,但是好资源还是有的。
简历
没想到还有很多人把包含个人信息的如此重要的简历也放在了Github上。搜索相关关键字resume
,简历
,学历
:
其它
比如说有时候我需要微信开放平台的应用AppID(太懒,不想申请),于是搜索关键字WXApi registerApp
,出来很多:
总之,鉴于越来越多人开始使用Github(非码农,比如说科学家,作家,音乐制作人,会计等职业),你可以在Github搜的内容也越来越多,不仅仅是代码,简直什么都有,什么某人做的笔记啊,写的小说啊,自拍照啊,还有书籍,论文等,简直出乎意料。
是时候该做点什么了
没错,当你看完本文,在以后上传项目代码时注意一下,以免泄露敏感信息;如果已经有帐号密码在上面了就赶快去修改吧。
文/Uri(简书作者)
原文链接:http://www.jianshu.com/p/d6b54f1d60f1
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。