不能想象没有书的世界

“不能想象没有书的世界” 2013 年的春天,游戏制作人(我最喜欢的)小岛秀夫写了一本《我所爱着的 MEME 们》,在书的开头,写着上面这句话。“而在 6 年之后的现在,这句话仍然不变”,他在新书《创作的基因》的序章里这样回应。 时间真的过的很快。 17 年刚毕业的时候,朋友送了我一本《穷查理宝典》,大概是体会到了阅读和思考的乐趣,于是在 18 年给自己定下了一个小目标:一年看 20 本书。幸运的是,最后这个目标成功达成了,但是在最后的一两个月,为了完成这个承诺,有点囫囵吞枣地翻阅了几本。所以在这之后,也不再给自己定类似的 kpi,而是随性地保持阅读的习惯,有兴致就每天翻看,累了就多喝凉水。 翻开一本新书,都会拍上一张照打个卡,从 1 到 20,不知不觉中,数字暂时停留在了 47。 我是如何挑书的呢? 其实就是两个字,“随缘”。多半是出于好奇,又或许是因为封面好看,也有些是来自很重要的朋友的推荐(所以如果有喜欢的书,记得分享给我)。 我好像有点固执的,几乎只看纸质书,而且翻开了就一定要看完。遇到感兴趣的书的时候,总是兴致匆匆,在多抓鱼上抢书锁定,凑几本包邮后立马下单,然后发现书箱里还没看的书越堆越高。 我会在 OneNote 上记笔记,摘抄引起我注意和思考的内容,像是给这本书标上自己的索引,方便日后回来查阅。但是时间久了,就容易忘记当时阅读的感受,只剩下笔记里干巴巴的一些引用,有的甚至一片空白。 于是开始写读书笔记,想要记录阅读后完整的体验,和一些想要分享的思考。《如何阅读一本书》里提到,看书的时候,要问问自己四个问题:这本书是讲什么的?怎么讲的?讲的好吗?和我有什么关系?看完书后,顺着“哲学四问”,在读书笔记里写下答案,也算是没有白白读过吧。 我从这些书里得到了什么? 看完《自下而上》会认为一个人改变不了什么,但是看完《人类群星闪耀时》又觉得每个人都在闪闪发光。学习《简单的博弈论》、《简单的逻辑学》,研究足球的时候学习《数字游戏》。看《故事写作大师班》、《风格练习》,想学习写作但是没学会。盯着酒杯发呆的时候会想起《存在主义咖啡馆》里的萨特,照镜子的时候偶尔会想起《局外人》。《守门员面对罚点球时的焦虑》我体会不到,《哥德尔、艾舍尔、巴赫》曾经打败过我。《禅与摩托车维修艺术》告诉我感性和理性的结合会是卓越,而《基本美》,就是基本美。 原来真的看了不少书,那浪费的这些时间也就不足为过,再多看一点书吧。 之前写读书笔记的时候,没有想好名字,现在有了答案。 “不能想象没有书的世界”...

March 31, 2021

Spring 事务事件监听,为什么会出现事务失效?

Spring 在 4.2 版本之后提供了@TransactionlEventListener 注解,可以很方便地在事务提交后做一些处理,但是如果使用不当,或者没有正确理解其背后的运行逻辑,很容易踩坑甚至导致线上故障。 之前工作中就遇到了一个问题,在事务监听时,做了一些事务操作,但是这个事务并没有生效。 今天我们就来深入了解一下,这个问题是怎么产生的,又该如何解决。 问题复现 我们来模拟一个很简单的场景:创建订单的时候会发布“订单已注册”的事件,在事件监听里保存操作记录,再发布“操作记录已保存”的事件,最后在这个事件监听里做一些逻辑。 以下代码中省略了一些不重要的实现。 首先是 OrderService,createOrder() 方法里保存订单记录,发布“订单已注册”的事件: public class OrderService { @Transactional public void createOrder() { String orderNo = "test_no"; Order order = new Order(orderNo); orderRepository.save(order); log.info("publish OrderCreatedEvent"); applicationContext.publishEvent(new OrderCreatedEvent(orderNo)); } } “订单已注册”的事件监听里,调用 operationService.saveOperation(): public class OrderCreatedEventListener { @TransactionalEventListener public void handle(OrderCreatedEvent event) { log.info("handle OrderCreatedEvent : " + event.getOrderNo()); operationService.saveOperation(event.getOrderNo(), "创建订单"); } } OperationService.saveOperation(),保存操作记录,并发布“操作记录已保存”的事件: public class OperationService { @Transactional public void saveOperation(String orderNo, String info) { Operation operation = new Operation(orderNo, info); operationRepository....

March 20, 2021

还没想好名儿的读书系列 | 《深度说服》

为什么要辩论? 是为了得到问题的答案吗?听双方激烈地争论爱情和面包哪个更重要,这个问题真的有答案吗? 答案自然是一句废话:“爱情和面包都很重要”,所以为什么要辩论呢? 如果你仔细想一想这个问题,可能会问问自己,爱情的定义是什么?爱情能给自己带来什么?在哪些情况下,财富更重要?在经过一系列缜密地思考、碰撞之后,你肯定也知道了答案“爱情和面包都很重要”,但是你对爱情和面包的理解,会更加清晰、深刻。如果真的到了爱情和面包的十字路口,相信你不再会一脸茫然,而是有信心和理由,坚定地走下去。 “辩论的目的是要找出有价值的观点,人们相信、接纳这个观点,从而产生更强的信念。” 这一本《深度说服》,就是教你如何思辨、反驳、提问和说服。 说起辩论,就有点双方互相博弈的意思,事实也是如此,书中的很多经验也都可以用博弈论来解释。当然,辩论的目的不仅仅是为了说服对方,现实生活中,也很难有机会与他人进行一场高质量、公平的“辩论”。辩论更重要的在于思考,在脑海中与自己进行“辩论”,最后得到的观点,就是你看待问题的方式,以什么样的方式看待问题,就能得到什么样的答案。表达能力也很重要,有时候觉得自己想明白了,但是支支吾吾就是说不清楚。我一直都觉得,说出来的言语是最能直接地体现一个人的思维方式和思考能力的,所以之前有和@92 一起录电台“怪话贩卖机”,一方面是为了满足自己的表达欲望,同时也是想借这个形式来锻炼自己的思维能力和表达能力。 针对思考、反驳、提问和综合说服,书中列举了对应的思路和原则,还很多实践方式,我就不再复述了。 既然是一本实践类的书,里面讲述的方法,真的有这么好用吗? 之前我自己没怎么接触过辩论,正好前段时间有朋友和我聊到奇葩说,书里也有提到,于是我就花了一点时间(都超过看完这本书的时间了!),挑了奇葩说里的一期,来简单复盘一下,看看辩论选手们有没有使用书中的技巧,效果到底怎么样。 下面我会将各位辩手的发言截取出来,并逐一分析,在括号里标注书中对应的技巧。 长图警告!!! 视频来源为奇葩说 S04E03,辩题是“外卖小哥惹毛我,该不该投诉他?”。 看到这个辩题,经常点外卖的你,拳头是不是已经暗暗握紧了? 不知道你是怎么想,反正蛋堡被惹毛后,软嘴唇都变硬了: 废话不多说,我们来看看奇葩说的各位辩手都怎么辩: 反方 - 卡姆 先由反方开始发言,卡姆的这段论述,主要表达了 3 个观点: 观点 1:“惹毛"这个词比较主观,行为让人生气,并不代表行为错误。 观点 2:投诉会让外卖小哥的努力白费。 观点 3:投诉可能会引起外卖小哥的报复。 中间穿插了一个女朋友的故事,以及“人在饿的时候最容易被惹毛”的信息支撑,增强了观点的代入感(综合说服 - 共情原则)。 正方 - 湉湉 正方湉湉开始反驳,“人在饿的时候最容易被惹毛”是明知故犯(归谬 - 抬升观点力度)。 然后引出自己观点 1: 投诉可以有效避免正面冲突。 接着反驳卡姆的观点 3 :投诉是匿名的(反驳 - 现象不存在),然后举自己打车的例子(综合说服 - 细节),以及表演“疯狗”的形象(综合说服 - 幽默 - 自损),增强了观点的可信度,也有不错的表演效果。 最后说明观点 2:投诉方便了那些不愿意投票的人。 反方 - 渐彪 首先反驳湉湉:投诉匿名的话,为什么会有报复性事件(反驳 - 现象不存在)。 然后话锋一转,“是情绪中的决定,为什么不留留手”,将讨论的重心转向理性思考和道德(反驳 - 受身)。 随后引出观点:外卖小哥和消费者权力悬殊。首先制造了价值冲突,然后对比鞭策和打击、挡刀和赶人,感情表达相当精彩(综合说服 - 制造感动)。...

February 18, 2021

英语学习回顾 - Stage One

说来也是奇妙,去年国庆从莫干山放空回来,突然就有了认真学好英语的想法。没有什么显而易见的缘由,总之是一种很强烈的“冲动”。倒也不能算作是冲动,它比冲动更坚毅,前提是能在转瞬即逝之前抓住它。 于是我立马联系了英语培训,下单买了全套的《English Grammar in Use》(英文原版有够贵哦)。“请问你是有打算考雅思或者托福?”,“我就是想学好英语”。怎样才算学好英语呢?这个目标不像考试驱动的学习,有个清晰明了的终点——分数。思考一下语言的本质,是信息交换的“等价物”,也是一套输入 -> 理解 -> 输出的系统。学好英语,就是构建好这一整套系统,从视听获得信息输入,理解并转化,然后通过言语输出,好的英语能力也能降低信息的折损率。当然之后也有计划考一下 PTE(Pearson Test of English Academic),可以全方位客观地验证下学习成果。 大致给自己安排了一整年的时间,时间紧,任务重,我开始找寻高效的方法论。《程序员学英语指南》 是一整套挺有干货的指南,里面有很多实用的学习方法。当然有一个好老师也很重要,big shout-out to my teacher Sissie! 截至目前已经学习了近四个月,接下来分享一下自己摸索出的学习内容和方法。 自然发音法 读书的时候学的英文发音,都是从认识音标开始的,遇到陌生的单词,没有音标的话都不知道怎么发音了,当然久而久之你会形成一些语感,去猜测单词大致的发音。其实母语为英语的外国人是不学音标的,从小就是模仿发音,身边也有大量的英文输入能帮助他们形成语感,纠正发音。音标是为了方便外语学习者才定义的。 上面说的语感就是“自然发音”,学习自然发音法就是掌握一套发音规则,例如字母 c + 字母 i/e/y,c 会发 s 的音。当然这套规则不能保证完全正确,因为英语中也有很多舶来词汇,或者特例。 如果遇到陌生的单词,就可以用自然发音法去猜测,然后验证,对于单词记忆和阅读还是很有帮助的。 词汇 没有什么好说的,词汇是基础,我也没什么记忆天赋,只能耐心背啦。艾宾浩斯遗忘曲线告诉我们遗忘的规律是先快后慢,所以需要间隔性重复记忆。我使用的记忆辅助软件是 Anki,以卡片的形式,按照给定的卡组和学习量,每天自动生成复习内容,而且开源跨平台,不过在 ios 上收费 163 元,我觉得还是挺值的哈。另外因为可以自定义卡组,Anki 也可以用来帮助记忆任何你想背的内容。 目前我在背的卡组是常用 7000 词,新单词先用自然发音法猜测,然后听发音确认并记忆,复习单词时自己先念出来,再校对。每天的背诵量就依个人情况量力而为,刚开始是每天 20 个新单词,近期调整为每天 50 个,半小时左右就能搞定新单词+复习。平时阅读遇到的陌生高频词我也会加入一个自定义的卡组,随机学习常用词+主动记忆高频词汇,效率还是挺不错的。 阅读 这一阶段有刻意地加大英文内容的输入量,主要还是以自己感兴趣的内容为主。我把 InfoQ 的站点切换成了英文版,有空时会翻一翻 Medium ,挑有兴趣的文章看一看,尽量找词汇量适合自己级别的,太多不认识的单词还是会带来一些挫败感。使用浏览器阅读的话还可以结合词典插件,顺带强化单词的记忆。 阅读除了浏览,还有朗读。平时没什么机会说英文的话,可以自己读文章。刚开始练习读的时候,读快了舌头会打架,连读时没有削掉辅音,而且没有什么腔调。看一些英语学习的资料上有推荐 shadow reading,但是我尝试了后觉得这一阶段做 shadow reading 还是有点难,TED 上有一期,史嘉琳教授分享了用 回音法 练习口语,感觉还挺合适的。后来意外地发现 Edge 浏览器的朗读功能很强,连读有消音,也有点腔调,还可以调整“工具人”的音色和速度。遇到好的文章就可以跟着 Edge 的朗读,用回音法一点点练习。 听力 听力也是日常英文输入的重要部分。基本上每天都会刷 Youtube,TED 的视频都是不错的听力练习材料,上下班的路上也会听一些 podcast。...

February 6, 2021

还没想好名儿的读书系列 | 《系统之美》

「他们可能会假定,通过系统分析,可以认清系统中的相互联系以及复杂纠葛,借助计算机的威力,最后找到预测和控制系统的钥匙。不幸的是,这是错误的观念,其根源在于工业时代根深蒂固的心智模式,即相信存在一把预测和控制的钥匙」 近期在系统分析领域进行了一些主题阅读,核心的理念基本没差,对于复杂系统,不要妄想用还原论解决,多观察系统的行为表现,判断整体趋势,调节并注重平衡。 相较于上一本《复杂》,这本书列出了系统分析领域常见的问题障碍、陷阱,以及相应的对策。 要素、连接和目标是构成系统的三要素,其中,改变要素对系统的影响最小,改变内在连接对系统的影响很大,系统的目标最不明显,但是最关键的决定因素。在足球世界中,如果球队的表现不好,换帅往往是第一选择 ( 今早兰帕德就被切尔西炒了鱿鱼 ) ,因为新的教练会带来不一样的技战术选择,调整球员之间的化学反应,而单单交易新的球员很难引发球队的质变。 之前在玩 FM ( Football Manager ) 时,会有点畏手畏脚,拘泥于一些技战术的教条,书中讲到“从系统的行为开始,强迫你自己关注于事实,而不是各种理论”,4-3-3 阵型的流行使得人人皆知“得中场者得天下”,但是贝尔萨的方形真空中场战术,带领利兹联近乎疯狂地升入英超,也是给主流足坛上了一课,“足球还是圆的”,对吧?游戏里提供了大量的数据统计分析,容易陷入数据陷阱,被水淹没,不知所措。虽然近些年数据科学发展迅速,但是目前 PA ( Performance Analysis ) 还不足以引导做出决策。数据分析的主要作用还是建立反馈回路,可以快速地看到决策带来的后果,例如 xG 值增加能反映进攻效率有所提升。 最近也有在深入学习、总结 DDD ( Domain-Driven Design ) ,DDD 的目标就是应对软件系统的复杂度。对应书中阐述的系统六大障碍,业务的需求就是“表象”,DDD 就是要拆解领域,分析、理解系统真正的内在结构,使用统一语言架起沟通的桥梁,然后划定清晰的上下文边界,以此来降低系统的复杂、混乱程度。 面对复杂系统自然没有“银弹”,但是这几本书能帮助我们更全面地思考、分析。就像哲学一样,系统分析能让你意识到问题,并很好地与之共存。 Trial and error, be humble. 以下是书中知识点的摘记,方便查阅。 系统的构成条件 要素、连接、功能或目标 一些关键词 系统基模 产生常见问题行为模式的系统结构 流入量 -> 存量 -> 流出量 人类的大脑似乎更加容易关注存量,关注流量的时候,更倾向于关注流入量 反馈回路 调节回路:保持存量稳定、趋向一个目标进行调节或校正 增强回路:强化系统原有的变化趋势 3 大特征 适应力 自组织 层次性 6 大障碍 别被表象所迷惑 在非线性的世界里 不要用线性的思维模式 恰好地划定边界 看清各种限制因素 无所不在的时间延迟 有限理性 8 大陷阱和对策 政策阻力:治标不治本 各个参与者的有限理性,目标不一致,任何一方增强的努力,导致其他所有人的努力也得到增强 对策:设法将各个子系统的目标协调一致,通常是设立一个更大的总体目标,让所有参与者突破各自的有限理性 公地悲剧 对策:教育和劝解,1....

January 26, 2021

Mockito饮用指南

介绍 Mockito是一个用于Java的开源测试mock框架,提供了非常清爽、简洁的API,这个名字来源于经典鸡尾酒 Mojito。 什么是mock?mock就是模拟,有了一个类或接口的定义,我们可以创建一个模拟对象来模拟它的行为,从而就不需要提供这个类或接口的真实实现。 这样在写单元测试的时候,我们只需要mock其他依赖,假设它们的预期返回,就可以专注于测试自己的实现逻辑。 听起来还不错吧,赶紧来尝一口试试。 先尝一口 引入 Mockito Mockito支持使用Gradle、Maven、Jar包引入,如果使用Spring Boot的话,spring-boot-starter-test默认已经集成了Mockito。 下文使用的Mockito,版本为3.6.0,项目代码基于Spring Boot。 入门操作 先来看一下官方文档上最简单的两个栗子 验证交互: import static org.mockito.Mockito.*; // mock创建一个 List List mockedList = mock(List.class); // 调用mock对象的方法 mockedList.add("one"); mockedList.clear(); // 可以直接验证方法被调用了 verify(mockedList).add("one"); verify(mockedList).clear(); mock调用返回: // mock创建一个 LinkedList LinkedList mockedList = mock(LinkedList.class); // 使用stub,假设mockedList.get(0)被调用时,会返回"first" when(mockedList.get(0)).thenReturn("first"); // 控制台会打印"first" System.out.println(mockedList.get(0)); // 控制台会打印"null",因为我们没有假设get(999)的返回值 System.out.println(mockedList.get(999)); 怎么样,看起来是不是很简单,语法也很贴近自然语言。 Mockito本质上是代理模式的应用,mock就是创建proxy对象,在proxy被调用前,使用stub的方式设置返回值,proxy还能记录并跟踪行为。 饮用搭配 doSomething() void方法或者spy对象,在mock行为时需要使用doThrow()、doAnswer()、doNothing()、doReturn()、doCallRealMethod() 注意区分mock和spy,mock就是完全代理,spy则是部分mock,可以调用真实方法,同时也能被跟踪验证 List list = new LinkedList(); List spy = spy(list); // 这里会抛出IndexOutOfBoundsException,因为调用了真实方法,而list实际上是个空列表 when(spy.get(0)).thenReturn("foo"); // 使用doReturn()来设置spy....

January 6, 2021

译 | 各种服务总线: 命令总线,服务总线和查询总线

本文为个人翻译,原文链接 上一篇文章讲了一个特殊的服务总线,即命令总线。现在我们回过头来看看其他服务总线,他们有什么相似或是不同之处。 什么是服务总线? 好像很难用简单的话来解释什么是服务总线。如果你在维基百科上查询服务总线,会看到一些关于企业级软件开发的术语定义,还列举了一些微软、IBM和Oracle的例子。这和我平时接触的PHP驱动的网络开发相比,完全是另一个世界。 我先用自己的话来总结一下什么是服务总线吧: 服务总线是组件之间传递信息的方式 消息是以DTO的形式,包含了需要执行的信息 “消息发送组件”会创建消息并传递给总线 “消息接收组件”会告知总线它想要消费的消息类型 当总线接收到消息时,会分发消息给接收者 总线是能够解耦组件的边界,发送者和接收者都不会感知到其他组件 由于解耦,总线能够让组件很高效地合作 而且因为总线是所有消息的“中间商”,可以给所有的消息增加功能,而不需要改动消息、发送者或接收者。例如给所有的消息加上日志打印或者给消息排序 希望这些描述能让你更清楚什么是服务总线。如果还不明白的话,可以看看我之前的文章,解释了什么是命令总线。那篇文章不会很抽象,而且有一些代码样例。 不同的总线 到目前为止,我们讨论了“通用的”服务总线。这个总线只会分发信息,无论如何也不会限定消息,或者是做一些处理。 你可以想象一下,不同类型的消息应该用不同的方式处理,这也是为什么我们有各种各样的服务总线,接下来我会讨论3种: 命令总线 查询总线 事件总线 我们来看一下它们的关键点 命令总线 消息(命令)标识了用户的意图。比如说“创建文章”或者“注册帐号”。 一个命令只能有一个确定的处理者 命令不会返回任何值 查询总线 消息(查询)标识了一次查询操作,注意不是数据库查询。比如“最新的文章”或者“文章的所有评论” 一个查询只能有一个确定的矗立着 查询会返回数据 查询不会改变应用的状态 事件总线 消息(事件)标识了一个已经发生的事件。比如“文章已创建”或者“用户已注册”。 一个事件可以有任意数量的处理者([0, inf])。 只会持有基础变量(字符串,整数值,布尔值),而不是整个类。 事件不会返回值 可以看到这些总线很相似,在我看来这也是他们很有用的原因。总线的概念很容易理解,使用也简单,可以给你的应用增强结构性和可预测性。 最后一点 验证 消息都应该被验证,这就意味着消息对象应该验证自己的入参,这样一来,只有有效的消息会被分发,也会带来一点限制。 “注册用户”命令应该需要(不考虑其他情况)一个用户名。这个命令应该要验证一下用户名是个字符串,并且字符的长度在6和100之间。而用户名是否唯一,这个需要由处理者来验证,而不是通过命令本身。 更大的模式 实现命令和查询是命令查询职责分离(CQRS)的一部分,当然你也可以不应用CQRS,直接使用服务总线。 命令和事件经常一起使用,比如“注册用户”命令完成后,会发送事件“用户已注册”。可以阅读 Matthias Noback 写的 From Commands To Events 。 拓展阅读 如果想更深入了解服务总线和消息: Mathias Verraes 写的 Messaging Flavours Robert Basic 写的 All Aboard The Service Bus Matthias Noback 写的 Some questions about the command bus 或者阅读我的其他文章:...

January 5, 2021

还没想好名儿的读书系列 | 《复杂》

前段时间刷到了关于这本书的读书笔记,来自公众号“安静的书桌”,里面提到对于复杂的问题,还原论没办法解释和解决,倒不如退一步,回归浑然的状态,观察其表现。因为自己一直习惯于解构问题,最近在一些系统性的问题思考和决策上有不少困惑,就忽然意识到可能已经踏入了陷阱,于是火速下单,花了一个周末读完了这本。 有趣的是,作者是侯世达的学生,你可能听说过《GEB》这本神书,侯世达就是它的作者。 书中给复杂系统的定义是,由大量组分组成的网络,不存在中央控制,通过简单运作规则产生复杂的集体行为和复杂的信息处理,并通过学习和进化产生适应性。联想一下蚂蚁和蚁群,每一只蚂蚁都只会一些简单的动作,但是汇集成蚁群的时候,它们甚至能牵线搭桥。现实中还有很多的复杂系统,如人体免疫系统、互联网等等,而且都具有涌现和自组织行为。 简单的单元之间互相连接、通讯,能产生复杂而难以想象的行为,或者说,意义?社交网络里有六度分隔理论,而在《死亡搁浅》里,Sam 努力要做的,就是连接一个个 Hub,连接一个支离破碎即将毁灭的世界。小岛秀夫 yyds! 随着对复杂系统的认知不断提高,也诞生了复杂性科学。涌现和自组织行为是如何产生的,这是复杂性科学的核心问题。书中讲述了其中的背景、历史,并且主要从计算的角度分析了一些复杂系统的特征。 另外还有几个很有趣的东西,遗传算法 GA、元细胞自动机(非冯诺依曼计算模型)、字符串类比程序 Copycat(关于计算机如何理解、类比)、还有网络模型,提到这些就有 GEB 内味儿了。 总的来说,这本书以科普为主,描述了复杂性科学的全貌。之前啃 GEB 花了一个多月,有种被打败的感觉,其实这本书很适合作为前菜,如果想更深入了解计算、思维、意识和 AI,再去看 GEB,应该会更容易接收和消化。 于我个人而言,《复杂》提醒了我,对于复杂的系统和问题,不能钻死脑筋去解构,妄想用还原论解释和解决一切,反而会损失关联产生的信息和意义。书里没有提到怎么处理复杂系统,“安静的书桌”的读书笔记里,认为可以回归浑然的状态,像黑盒一样去观察系统的表现,倒也没错,“如果一只动物走路像鸭子而且嘎嘎叫,那它就是鸭子”,我喜欢这一句。 对于复杂的事物,还原论可能是陷阱,要聚焦于平衡。 传送门 豆瓣 - 《复杂》 好书一起读 (453):《复杂》...

December 28, 2020