如何提反馈

引子

设想你是一位老师,正看到某人托着腮听课,心中会有怎样的解读?可能会有以下两种:一种是这个人不认真听讲,一种是这个人牙疼。这两种解读又分别产生不同的判断:前者是这个人不喜欢你的课,令你心生不悦;后者是这个人生病了,于是你对他的配合心存感激。

这两种判断会让你产生两种完全不同的行为模式,比如:你可能以后不想邀请他来参加你的课了;或者你会上前关切地询问他是不是生病了。

为什么会这样呢? 如果我们试着探寻背后的原因,会发现人们对事物的认知分为三个阶段:

  • 描述(Description):客观存在的事实,不包含任何个人猜测
  • 解读(Interpretation):根据场景进行的猜测,每个人的解读是不同的
  • 判断(Evaluation):带有个人感情色彩的解读

我们需要不断地提醒自己:不能因为自己的解读和判断曲解了客观事实。很多时候我们应该遵循确认、验证、沟通、反馈的过程,以此来核实解读是否正确,从而作出正确的判断。

什么是反馈?

在工作情景中,反馈是团队分享信息、了解工作表现或工作关系中某些环节的过程。在ThoughtWorks胜任力模型中提到:我们提出的反馈,应该是基于行为的、具体的、有帮助的反馈。在《伙伴教练》一书中有类似的定义:反馈着重在三个方面,行为、行为对人际关系的影响与行为对结果的影响。

莎士比亚说:“一千个读者眼中就会有一千个哈姆雷特”。必须承认我们对事物的认知是不同的,因为它与每个人的家庭、教育背景、工作经验、所处环境相关联。值得注意的是,尽管我们对于某个事件的描述不可避免地会有个人解读,但是具有感情色彩的判断是不建议的,尤其是带有负面感情的判断。因为它会使“反馈”的效果大打折扣,甚至出现负面效果。

这就要求提供反馈的人实事求是,反馈的内容是对方的行为,不要增加任何的主观判断。

如何提反馈呢?

征得对方同意

反馈不是一个单方面的活动,对方的接受程度会直接影响到反馈的效果。因此,征得对方同意是反馈开始的第一步。试想,当对方正在全身心投入的讨论需求、或者正在忙于解决线上问题时,会有耐心听取反馈吗?显然这些都不是给对方提供反馈的恰当时机。

一对一沟通

一对一沟通是比较推荐的提供反馈的方式。它会让提供和接受反馈的双方感到更加轻松和安全,在这种环境下,提出的反馈会更容易被接受。不仅如此,一对一沟通还能激发信任感,使双方进行更深层次的交流。

反馈的技巧

注意表达方式,就事论事,不要把自己的情绪带进去。有两个不错的反馈模板,分别是IIY模型(I See……I Feel……You Think)和SBIR模型(Situation在什么情况下……Behavior观察到的行为……Impact造成的影响……Reflection反思)。拿IIY模型举例,我们可以通过这样的三步为反馈提供合适的对话窗口:首先描述一个事实,这个事实是我们自己的观察(I See);再而基于共同的上下文对该事实进行解读,并提出自己的看法(I Feel);最后和对方确认是否接受这样一个事实和看法(You Think)。

我们来设想这个场景:有位同学连续几天项目站会都迟到。

项目经理:“你连续几天都迟到,是不是不想参加站会了?你的工作态度有问题。”
项目经理:“你连续几天都迟到,是不是遇到了什么困难?如果有困难,大家能提供什么帮助吗?”

对于以上两种情形,显而易见后者更容易被这位同学接受。如果从接受者的角度来看待反馈,我们可以引入一个防御模型来解释同样的问题。

反馈防御模型

反馈防御模型有外,中,内三层分别为:行为,态度和价值观/信仰,内层对反馈的防御大于外层。当对方在接受反馈时,如果内容仅仅包含行为的事实,那么这将是一个较为容易接受的反馈,若是包含对态度、乃至价值观的反馈,其效果可想而知。

写在后面

心理学中有个沟通小技巧,叫做“情绪三明治”,即“谈情、说爱、讲理”。在沟通的过程中,要遵循三个步骤:先谈自己的感受,再表达自己对对方的关心和爱,最后再讲自己的看法、道理或建议。

我们要留意的是“沟通的意义取决于对方的回应”。假如对方已经不悦了,说明我们需要即时调整方式和方法,对方的情绪和行为就是信号。沟通本身没有对与错,只有“有效”或者“无效”。因此,自己说得多么“正确”没有意义,对方接受你想表达的信息才有意义。

写到这里,突然意识到,最近一段时间,我与儿子的沟通似乎少了以往的平和和耐心。因为我自身在生活、工作中的压力,混杂了对他的期望与不满,让我对儿子”不听话”作了更多不理智的解读,我的反馈也更多地针对他的态度而不是行为本身,使得他对我越来越抵触。

现在,我应该思考如何给他更好地提出反馈了。


更多精彩洞见,请关注微信公众号:ThoughtWorks洞见

Share

你要专业

在我不算长的职业生涯中,有很多同事都给过我正面的评价(当然,可能有更多负面的评价,不过我都选择性的遗忘掉了)。有人欣赏我的命令行技巧,有人则称赞我代码写的比较快,有人说我的Vim的插件配置的很高效,还有人说坐在我旁边写PPT效率会变高。而我自己最喜欢一个评价是:有我在项目上的时候,团队就会觉得安心。

我喜欢这个评价是因为它是我希望自己能展现的一个状态:成为一个靠谱的职场人。关于靠谱的职场人,《重来》的作者之一Jason有一个很精炼的描述:

Work ethic is about showing up, being on time, being reliable, doing what you say you’re going to do, being trustworthy, putting in a fair day’s work, respecting the work, respecting the customer, respecting the organization, respecting co-workers, not wasting time, not making work hard for other people, not creating unnecessary work for other people, not being a bottleneck, not faking work. Work ethic is about being a fundamentally good person that others can count on and enjoy working with. — Jason Fried

大意是:职业精神就是可靠,言出必行,尊重工作,不要浪费时间,不给别人添麻烦等等。不过,精炼的描述往往失之可操作性不够强。就好比知道“高内聚,低耦合”并不能帮助你写好代码一样。在这篇文章里,我打算举一些日常工作中常见的例子,尝试通过Specification By Example的方式给靠谱的职场人下一个定义。

在职场中,靠谱的意思就是:成为一个别人会信赖并乐于和你一起工作的人。而人们喜欢和靠谱的人一起工作。

尊重你的工作

人们站在不同的立场看待同一件事情时,必然会产生分歧。这几乎在项目中随处可以见,比如对于某个Feature,客户认为必须实现,这样在年终的绩效考核中才有令领导满意的成绩;另一方面,对于交付团队来说,技术上难以实现或者工作量太大,难以在有限的时间内完成;开发团队钟爱新的流行的技术栈,而客户则相对保守,需要考虑培训成本等等其他方面的因素。如果再加上沟通不畅,很容易导致团队士气低落,甚至双方互相抱怨。

一份工作,既是企业(企业中的员工)赖以生存的条件,又是企业和服务对象共同为社会创造价值的载体。这个产品可以解决某些人的问题,提高效率,为社会带来价值。工作本身就是值得尊重的。如果你被指派到一个项目上,并且作为个人,你不认为这个项目违背了自己的价值观,那就应该全力以赴去完成它。另一方面,如果你认为项目的价值和自己的三观不匹配(比如,如果有人出钱让你去开发GFW之类的工具;或者一个工具用来监控员工的桌面等),你完全可以选择不去参加这个项目。

新人常犯的一个错误是将遵循既有规则当成尊重。举个例子,在实际交付的时候,团队中的Tech Lead做出了一项你任务是错误的决策,作为团队成员,你可以据理力争,表达自己的看法,并提出自己的提议。另一方面,你也可以假装这个决策是超出你控制范围的,并且假装它是正确的,然后低头来按照决策来实施。在我看来,第二种看似尊重的行为是对团队和项目的非常的不尊重。退而言之,即使你的提议被拒掉,你也可以从中学到很多之前看不到的知识。一方面可以帮助你自己理清思路,看到自己方案的缺点,另一方面,你可以学习人们在实际项目中,如何对于不同的条件来做妥协。

与他人合作

一个人可以走的更快,一群人可以走的更远 — 非洲谚语

我们的日常工作中,总避免不了要和不同角色,不同经验的人一起工作。和单枪匹马独自作战不同的是,在一个群体中,个人的行为需要遵循一定的约定,需要和其他团队成员一起配合来完成任务。换句话说,要有团队合作精神。在和其他人一起工作时,第一要义是不要为别人制造更多的工作量。事实上,你应该在自己能力允许的前提下,尽可能让别人做最少的工作。相信我,你会希望自己和这样的人合作的。

我们从一个假象出来的场景出发,来看看如何在类似的场景中展现专业性:

CI服务器地址

设想这样一个场景:你所在的团队有一个微信群,大家很多问题都在群里提出并讨论。但是一些简单问题可能被频繁的问道,比如XXX服务器的地址是啥?前两天又有一个新人加入了团队,他今天在群里问了一个之前被回答过好几次的问题:谁知道CI服务器的地址,请发给我一下,多谢!

而这时候,你可以做什么呢?“我之前发过邮件给所有人了,你翻一下邮件”。

这是一个可行的方案,但是需要

  • 邮件标题的关键字 或者
  • 邮件正文关键字

而要做搜索本身动作,他需要打开邮件客户端或者在浏览器打开Web Mail,如果是访问Web Mail,他可能还要登录公司内网(输入密码),还可能要查看Okta推送的消息,这又需要打开手机,切换到Okta,点击确认。如果工作环境网络有vpn的隔离,情况可能会更复杂。

如果你花费1分钟帮他搜一下,然后把地址发给他,并告诉他用户名/密码就是域账号的用户名/密码,效果则会好很多。

访问外部API

如果我们把这个场景在稍微复杂化一点:有人在微信群问如何在Postman中访问获取所有网点信息的API。要访问这个API,客户端需要一个Endpoint和一些特定的HTTP Header。最简单的做法是发送一个截图。

不幸的是,Header中有一个叫做x-api-token,它的值是一个256个字符的hash。这时候图片就变得毫无用处了:你不会期望问问题的那个人用手敲一遍token吧?另一个方法是把API的Endpoint和所需的HTTP Header是分别以文本形式发送给他,(如果这个API需要多个header的话,你可能要复制粘贴好几次)。

再进一步,你可以通过一个cURL加命令行参数的方式将这些内容一次性发送给他:

$ curl -H "x-api-token: token" -H "Accept:application/json" https://host:port/top-security-resources/1

嗯,挺不错的。不过如果明天又有其他人问你要这个API的访问方式,你又要再来一遍,还是比较麻烦。你稍微翻了下Postman的帮助,发现它支持导出,还可以定义一些环境参数等。如果将这些内容都导出出来,然后放在代码仓库中,其他所有人就无需每次都找人要各种URL了。

显然,最后一种方法既满足当前的需求,又有很好的可扩展性,这样你就通过一个具体的问题,抽象出一个高阶的问题,并且为这个问题提供了一个可行的解决方案了。当然,这种方法的缺陷是会占用你很多时间,需要你学习额外的知识。不过,如果是我,我肯定愿意选择这种方式。

帮助团队里的测试

我记得我们曾经有一个研发平台项目,其中一个需求是实现租户代码坏味道识别的工具:这个工具的输入是平台上程序员提交的源代码,然后我们的工具会分析类和类之间的关系,然后给代码评定一个分数:比如集成层次不能太深,不能有多继承之类。代码本身并不难写,但是要测试时需要列举很多case,每一个case至少需要可以能编译通过,这要求测试同事还要会写合法的C++代码,也就意味着他们还需要C++的多继承,抽象类之类。

我花了一些时间(两个小时左右)为测试同事写了一个小工具:通过指定一些参数,比如类的继承层次,是否多继承之类,然后这个工具帮你生成一堆合法的可以编译通过的源代码:包括多个文件,文件之间的引用(比如头文件中定义接口,然后在实现代码中访问这些接口等等)。测试可以很容易通过它来生成测试用例,然后再来验证待验证的工具到底能不能识别这些坏味道。

自动化工具

另外一个有趣的例子是:很久前的一个项目,团队在开发一个大表单,大表单里有很多问题,大概分为三个页面:第一页需要填写一些个人信息,比如姓甚名谁家住何处等,第二页则会填更多的信息,而第二页的很多问题跟第一页相关,一些问题只需要在第一页选了A选项才会出现,而另外一些问题则仅仅为B场景设计等等。

然后某个新需求是在第三页添加一个新的问题(根据第二页的某个回答来决定要不要显示出来)。在实现过程中,开发人员需要手工填很多内容才能到达第三页,而这个过程还可能出错(比如第二页中需要去调用某个API来生成下拉列表内容,那个API有可能会挂掉),一出错又得重来一次(当时还没有HMR啊,State管理啊这些高级货,只能重新刷新)。团队意识到这个痛点之后,有人就开发了一个Chrome的扩展,这个扩展可以根据预设的答案自动填写表单(好像是模拟鼠标点击的方式),直到你想要停下来的那一个问题。这样就将开发中调试的时间大大缩短,还可以节省很多测试同事的工作量。

事实上,这类的场景在实际项目里会有很多。通过一些自己的努力,让团队里的其他角色、或者团队之外的你的下游系统、又或者未来的系统维护者的生活变得轻松一些,是靠谱的一个重要表现形式。

做好Desk Check

在敏捷开发中,当一个Story开发结束之后,我们会把开发,测试,BA,UX聚在一起来做Shoulder Check / Desk Check。这个实践可能很多团队都会坚持。但是做的流畅程度则千差万别,效果也自然大相径庭。

我现在还记得第一次做Desk Check时候的忙乱,由于事先没有准备好,当围观群众上来之后,我和peer还没有把Jira上的卡打开。当逐条过验收条件(AC)的时候,我们才发现漏掉了一条。然后在跨浏览器检查的时候,发现在Safari里页面上有个按钮在点击时毫无响应,这时候我和peer打开了dev-tools开始了现场debug等等。

在后来的项目中,我特别注重这个实践,努力让Desk Check变得流畅无碍。你前期准备的越充分,在Desk Check的时候就越顺畅。比如,在把所有人都召集过来之前,自己先把所有AC过一遍,如果有Feature测试的话,就把测试用例大概过一遍,看看能不能覆盖所有AC。在Check的开始前,先把Story描述一遍,特别是业务场景,业务价值。这个过程可以对着Jira卡来过,如果有新的讨论,也需要顺手同步到Jira的comments里,以便未来参考。由于参加Desk Check的QA和BA可能手头都有很多任务并行处理,所以你需要快速的将上下文分享出来,让大家在同一理解水平上,这样后续的Check才有可能顺畅。

如果Story涉及跨浏览器,那么你最好可以将各个浏览器都打开,而且切换到需要showcase的页面上。这些前期的准备工作,可以减少参与者的上下文切换成本,可以让大家迅速进入到验收中,而且出错甚少。事实上,大部分问题在你的准备中都已经解决了,剩余的小问题则可以在后续的Story开发过程中顺手修复。

测试数据准备

BA对系统的理解是基于现实世界中的业务来的,因此在数据准备上一定要小心,比如10位数字的身份证号码,带有字母的手机号等等。即使是测试数据,也应该认真对待,尽量避免使用随机的文本来填写表单,否则结果页面看起来会非常不专业。

在有些场景下,比如你需要测试当文本超过一定长度会显示省略号,你仍然需要仔细设计文本,让其看起来更为实际。当你读到一个名称为“锟斤拷锟斤拷锟斤拷…”的产品时,你会做何感想?

事实上,一些工具可以帮你简化测试数据的准备,而且可以确保专业性。比如Ruby里的Faker(Perl中的Data:Faker的Ruby移植版)https://github.com/stympy/faker,它可以帮你生成很多常见的数据实例,比如

require 'faker'

Faker::Name.name      #=> "Christophe Bartell"
Faker::Bank.iban #=> "GB76DZJM33188515981979"
Faker::Internet.email #=> "kirsten.greenholt@corkeryfisher.info”

使用类似的工具,只需要编写一些微小的代码,就可以生成更贴近业务场景的测试数据,从而让显得更加专业。

设计中的细节

同样的道理,UX在设计稿中,需要考虑很多细节,比如

  • 常见的拼写错误
  • 业务术语的正确使用
  • 理解数字的含义
  • 视觉一致性(字体的选用,相同元素的字号,颜色暗示等)

这些细节可以让观众体会到你的认真和用心,而由于视觉的特殊性,一个微小的纰漏都可能被放大成严重的问题。比如在某一份设计中,所有的标题都采用24号深灰色的Consolas字体,但是在另一处,字号变成了18,而且加粗了。这种错误很容易别识别,从而让人产生不好的印象。

业务语言

而对于用错术语,或者没有完全理解业务时,对数字的解读则会产生更严重的问题。比如在广告行业,广告主比较关心的一个指标是Frequency,计算Frequency的方式是用PV(Page View)/UV(Unique View),也就是每个用户的平均点击量。页面的总访问量肯定比访问这些页面的人数要多(抽屉原理),那么它们的比值也肯定会大于1。如果UX不理解这个业务含义,可能会在设计稿上标识0.68之类的数字。

类似的,如果你在绘制一个Pie Chart,那么最起码所有部分之和加起来要等于100%,而且大致的占比要正确,比如应该避免出现:数值为25%但是视觉上比例却接近1/3等等。

对开发者友好

此外,在设计稿中,能为不同的场景设计出相应的变体(variation)也会大大降低开发者和UX之间来回讨论的工作量。比如在一个产品列表的设计中,列表中的第一个条目展示正常情况(happy path),而第二个显示当某些元素缺失时的展现(空值,非法值等),而第三个条目显示当标题超长之后是应该折行还是显示省略号等。

每一个细节事实上都在为你的靠谱程度打分,也会潜在的影响别人是否愿意信赖并乐于与你一同工作。

小结

文章中列举了一些实际项目中的例子,有关于如何做好开发实践的技巧,有关于帮助团队里的其他人更方便工作的意识,有关于对开发者友好的设计细节。所有这些例子中的技巧,事实上都与这样一个事实有关:要在职场中成为一个靠谱的人,意味着即使对团队内部,你也需要扮演一个专业服务者的角色。你需要更多的站在他人的角度来考虑问题,在合理的范围内,尽量的减少别人和你合作时的工作量。此外,你需要处理好很多细节,职业性体现在很多的细节中,从测试数据中的asdfasdf,到设计稿中的typo,都可能暴露你是否在用心对待工作。

文中提及的这些值得践行的技巧事实上与具体技术关联甚弱,你可以很容易的举一反三,并在实际场景中灵活运用,成为一个专业而靠谱的职业人。


更多精彩洞见请关注微信公众号:ThoughtWorks洞见

Share

RxJS 快速入门

这是一篇给新手的 RxJS 快速入门,它可能不精确、不全面,但力求对新手友好。

异步与“回调地狱”

我们都知道 JavaScript 是个多范式语言,它既支持过程式编程,又支持函数式编程,两者分别适用于不同的场合。在同步环境下,两者各有优缺点,甚至有时候过程式会更简明一些,但在异步环境下(最典型的场景是一个 Ajax 请求完成后紧接着执行另一个 Ajax 请求),由于无法控制执行和完成的顺序,所以就无法使用传统的过程式写法,函数式就会展现出其优势。

问题在于,传统的函数式写法实在太不友好了。

传统写法下,当我们调用一个 Ajax 时,就要给它一个回调函数,这样当 Ajax 完成时,就会调用它。当逻辑简单的时候,这毫无问题。但是我要串起 10 个 Ajax 请求时该怎么办呢?十重嵌套吗?恩?似乎有点不对劲儿!

这就是回调地狱。

不仅如此,有时候我到底需要串起多少个 Ajax 请求是未知的,要串起哪些也同样是未知的。这已经不再是地狱,而是《Mission: Impossible》了。

我,承诺(Promise),帮你解决

事实上,这样的问题早在 1976 年就已经被发现并解决了。注意,我没写错,确实是 1976 年。

承诺,英文是 Promise [ˈprɑmɪs],它的基本思想是借助一个代表回执的变量来把回调地狱拍平。

我们以购物为例来看看日常生活中的承诺。

  1. 你去电商平台下单,并付款
  2. 平台会给你一个订单号,这个订单号本质上是一个回执,代表商家做出了“稍后我将给你发货”的承诺
  3. 商家发货给你,在这个过程中你不用等待(异步)
  4. 过一段时间,快递到了
  5. 你签收(回调函数被调用)商品(回调参数)
  6. 这次承诺结束

这是最直白的单步骤回调,如果理解了它,再继续往下看。

你跟电商下的单,但是却从快递(并不属于商家)那里接收到了商品,仔细想想,你不觉得奇怪吗?虽然表面看确实是商家给你的商品,但我们分解开中间步骤就会发现还有一些幕后的步骤。

  1. 商家把商品交给快递公司,给快递公司一个订单号(老的回执)并拿回一个运单号(新的回执)
  2. 快递公司执行这个新承诺,这个过程中商家不用等待(异步)
  3. 快递公司完成这个新承诺,你收到这个新承诺携带的商品

所以,事实上,这个购物流程包括两个承诺:

  1. 商家对你的一个发货承诺
  2. 快递公司对商家的运货承诺

因此,只要把这些承诺串起来,这些异步动作也就同样串起来了。

当我们把每个承诺都抽象成一个对象时,我们就可以对任意数量、任意顺序的承诺进行组合,变成一个新的承诺。因此回调地狱不复存在,前述的 Mission 也变得 Possible 了。

Promise 的缺点

Promise 固然是一个重大的进步,但在有些场景下仍然是不够的。比如,Promise 的特点是无论有没有人关心它的执行结果,它都会立即开始执行,并且你没有机会取消这次执行。显然,在某些情况下这么做是浪费的甚至错误的。仍然以电商为例,如果某商户的订单不允许取消,你还会去买吗?再举个编程领域的例子:如果你发起了一个 Ajax 请求,然后用户导航到了另一个路由,显然,你这个请求如果还没有完成就应该被取消,而不应该发出去。但是使用 Promise,你做不到,不是因为实现方面的原因,而是因为它在概念层(接口定义上)就无法支持取消。

此外,由于 Promise 只会承载一个值,因此当我们要处理的是一个集合的时候就比较困难了。比如对于一个随机数列(总数未知),如果我们要借助 Web API 检查每个数字的有效性,然后对前一百个有效数字进行求和,那么用 Promise 写就比较麻烦了。

我们需要一个更高级的 Promise。

Observable

它就是可观察对象(Observable [əbˈzɜrvəbl]),Observable 顾名思义就是可以被别人观察的对象,当它变化时,观察者就可以得到通知。换句话说,它负责生产数据,别人可以消费它生产的数据。

如果你是个资深后端,那么可能还记得 MessageQueue 的工作模式,它们很像。如果不懂 MQ 也没关系,我还是用日常知识给你打个比方。

Observable 就像个传送带。这个传送带不断运行,围绕这个传送带建立了一条生产线,包括一系列工序,不同的工序承担单一而确定的职责。每个工位上有一个工人。

整个传送带的起点是原料箱,原料箱中的原料不断被放到传送带上。工人只需要待在自己的工位上,对面前的原料进行加工,然后放回传送带上或放到另一条传送带上即可,简单、高效、无意外 —— 符合程序员的审美。

而且这个生产线还非常先进 —— 不接单就不生产,非常有效地杜绝了浪费。

FRP

这种设计,看上去很美,对吧?但光看着漂亮可不行,在编程时要怎么实现呢?实际上,这是一种编程范式,叫做函数响应式编程(FRP)。它比 Promise 可年轻多了,直到 1997 年被人提出来。

顾名思义,FRP 同时具有函数式编程和响应式编程的特点。响应式编程是什么呢?形象的说,它的工作模式就是“饭来张口,衣来伸手”,也就是说,等待外界的输入,并做出响应。流水线每个工位上的工人正是这种工作模式。

工业上,流水线是人类管理经验的结晶,它所做的事情是什么呢?本质上就是把每个处理都局部化,以减小复杂度(降低对工人素质的要求)。而这,正是软件行业所求之不得的。响应式,就是编程领域的流水线。

那么函数式呢?函数式最显著的特征就是没有副作用,而这恰好是对流水线上每个工序的要求。显然,如果某个工序的操作会导致整个生产线平移 10 米,那么用不了多久这个生产线就要掉到海里了,这样的生产线毫无价值。

因此,响应式和函数式几乎是注定要在一起的。

ReactiveX

2012 年,微软 .NET 开发组的一个团队为了给 LinQ 设计扩展机制而引入了 FRP 概念,却发现 FRP 的价值不止于此。于是一个新的项目出现了,它就是 ReactiveX。

严格来说 ReactiveX 应该是一 FRP 库,因为它几乎在每个主流语言下都提供了实现,而且这些实现都是语言原生风格的,不是简单地迁移。如果你在任何语言下用过带有 Rx 前缀的库,那多半儿就是 ReactiveX 的一个实现了,如 RxJava、Rx.NET、RxGroovy、RxSwift 等等。

ReactiveX 本身其实并不难,难的是 FRP 编程范式以及对操作符(operator)的理解。所以,只要学会了任何一个 Rx* 库,那么其它语言的库就可以触类旁通了。

宝石图

为了帮助开发者更容易地理解 ReactiveX 的工作原理,ReactiveX 开发组还设计了一种很形象的图,那就是宝石图。这货长这样(英文注释不必细看,接下来我会简单解释下):

中间的带箭头的线就像传送带,用来表示数据序列,这个数据序列被称为“流”。上方的流叫做输入流,下方的流叫做输出流。输入流可能有多个,但是输出流只会有一个(不过,流中的每个数据项也可以是别的流)。

数据序列上的每个圆圈表示一个数据项,圆圈的位置表示数据出现的先后顺序,但是一般不会表示精确的时间比例,比如在一毫秒内接连出现的两个数据之间仍然会有较大的距离。只有少数涉及到时间的操作,其宝石图才会表现出精确的时间比例。

圆圈的最后,通常会有一条竖线或者一个叉号。竖线表示这个流正常终止了,也就是说不会再有更多的数据提供出来了。而叉号表示这个流抛出错误导致异常中止了。还有一种流,既没有竖线也没有叉号,这种叫做无尽流,比如一个由所有自然数组成的流就不会主动终止。但是要注意,无尽流仍然是可以处理的,因为需要多少项是由消费者决定的。你可以把这个“智能”传送带理解为由下一个工位“叫号”的,没“叫号”下一项数据就不会过来。

中间的大方框表示一个操作,也就是 operator —— 一个函数,比如这个图中的操作就是把输入流中的条目乘以十后放入输出流中。

看懂了宝石图,就能很形象的理解各种操作符了。

RxJS

主角登场了。RxJS 就是 ReactiveX 在 JavaScript 语言上的实现。对于 JavaScript 程序员来说,不管你是前端还是 NodeJS 后端,RxJS 都会令你受益。

由于 JavaScript 本身的缺陷,RxJS 不得不采用了很多怪异的写法。它对于 Java / C# 等背景的程序员来说可能会显得比较怪异,不过,你可以先忽略它们,聚焦在编程范式和接下来要讲的操作符语义上。

典型的写法

of(1,2,3).pipe(
  filter(item=>item % 2 === 1),
  map(item=>item * 3),
).subscribe(item=> console.log(item))
`</pre>

它会输出:
<pre>`3
9

其中 of 称为创建器(creator),用来创建流,它返回一个 Observable 类型的对象,filter 和 map 称为操作符(operator),用来对条目进行处理。这些操作符被当作 Observable 对象的 pipe 方法的参数传进去。诚然,这个写法略显怪异,不过这主要是被 js 的设计缺陷所迫,它已经是目前 js 体系下多种解决方案中相对好看的一种了。

Observable 对象的 subscribe 方法表示消费者要订阅这个流,当流中出现数据时,传给 subscribe 方法的回调函数就会被调用,并且把这个数据传进去。这个回调函数可能被调用很多次,取决于这个流中有多少条数据。

注意,Observable 必须被 subscribe 之后才会开始生产数据。如果没人 subscribe 它,那就什么都不会做。

简单创建器

广义上,创建器也是操作符的一种,不过这里我们把它单独拿出来讲。要启动生产线,我们得先提供原料。本质上,这个提供者就是一组函数,当流水线需要拿新的原料时,就会调用它。

你当然可以自己实现这个提供者,但通常是不用的。RxJS 提供了很多预定义的创建器,而且将来可能还会增加新的。不过,那些眼花缭乱的创建器完全没必要全记住,只要记住少数几个就够了,其它的有时间慢慢看。

of – 单一值转为流

它接收任意多个参数,参数可以是任意类型,然后它会把这些参数逐个放入流中。

from – 数组转为流

它接受一个数组型参数,数组中可以有任意数据,然后把数组的每个元素逐个放入流中。

range – 范围转为流

它接受两个数字型参数,一个起点,一个终点,然后按 1 递增,把中间的每个数字(含边界值)放入流中。

fromPromise – Promise 转为流

接受一个 Promise,当这个 Promise 有了输出时,就把这个输出放入流中。

要注意的是,当 Promise 作为参数传给 fromPromise 时,这个 Promise 就开始执行了,你没有机会防止它被执行。

如果你需要这个 Promise 被消费时才执行,那就要改用接下来要讲的 defer 创建器。

defer – 惰性创建流

它的参数是一个用来生产流的工厂函数。也就是说,当消费方需要流(注意不是需要流中的值)的时候,就会调用这个函数,创建一个流,并从这个流中进行消费(取数据)。

因此,当我们定义 defer 的时候,实际上还不存在一个真正的流,只是给出了创建这个流的方法,所以叫惰性创建流。

timer – 定时器流

它有两个数字型的参数,第一个是首次等待时间,第二个是重复间隔时间。从图上可以看出,它实际上是个无尽流 —— 没有终止线。因此它会按照预定的规则往流中不断重复发出数据。

要注意,虽然名字有相关性,但它不是 setTimeout 的等价物,事实上它的行为更像是 setInterval

interval – 定时器流

它和 timer 唯一的差别是它只接受一个参数。事实上,它就是一个语法糖,相当于 timer(1000, 1000),也就是说初始等待时间和间隔时间是一样的。

如果需求确实是 interval 的语义,那么就优先使用这个语法糖,毕竟,从行为上它和 setInterval 几乎是一样的。

思考题:假设点了一个按钮之后我要立刻开始一个动作,然后每隔 1000 毫秒重复一次,该怎么做?换句话说:该怎么移除首次延迟时间?

Subject – 主体对象

它和创建器不同,创建器是供直接调用的函数,而 Subject 则是一个实现了 Observable 接口的类。也就是说,你要先把它 new 出来(假设实例叫 subject),然后你就可以通过程序控制的方式往流里手动放数据了。它的典型用法是用来管理事件,比如当用户点击了某个按钮时,你希望发出一个事件,那么就可以调用 subject.next(someValue) 来把事件内容放进流中。

当你希望手动控制往这个流中放数据的时机时,这种特性非常有用。

当然,Subject 其实并没有这么简单,用法也很多,不过这部分内容超出了本文的范围。

合并创建器

我们不但可以直接创建流,还可以对多个现有的流进行不同形式的合并,创建一个新的流。常见的合并方式有三种:并联、串联、拉链。

merge – 并联

从图上我们可以看到两个流中的内容被合并到了一个流中。只要任何一个流中出现了值就会立刻被输出,哪怕其中一个流是完全空的也不影响结果 —— 等同于原始流。

这种工作方式非常像电路中的并联行为,因此我称其为并联创建器。

并联在什么情况下起作用呢?举个例子吧:有一个列表需要每隔 5 秒钟定时刷新一次,但是一旦用户按了搜索按钮,就必须立即刷新,而不能等待 5 秒间隔。这时候就可以用一个定时器流和一个自定义的用户操作流(subject)merge 在一起。这样,无论哪个流中出现了数据,都会进行刷新。

concat – 串联

从图中我们可以看到两个流中的内容被按照顺序放进了输出流中。前面的流尚未结束时(注意竖线),后面的流就会一直等待。

这种工作方式非常像电路中的串联行为,因此我称其为串联创建器。

串联的适用场景就很容易想象了,比如我们需要先通过 Web API 进行登录,然后取学生名册。这两个操作就是异步且串联工作的。

zip – 拉链

zip 的直译就是拉链,事实上,有些压缩软件的图标就是一个带拉链的钥匙包。拉链的特点是两边各有一个“齿”,两者会啮合在一起。这里的 zip 操作也是如此。

从图上我们可以看到,两个输入流中分别出现了一些数据,当仅仅输入流 A 中出现了数据时,输出流中什么都没有,因为它还在等另一个“齿”。当输出流 B 中出现了数据时,两个“齿”都凑齐了,于是对这两个齿执行中间定义的运算(取 A 的形状,B 的颜色,并合成为输出数据)。

可以看到,当任何一个流先行结束之后,整个输出流也就结束了。

拉链创建器适用的场景要少一些,通常用于合并两个数据有对应关系的数据源。比如一个流中是姓名,另一个流中是成绩,还有一个流中是年龄,如果这三个流中的每个条目都有精确的对应关系,那么就可以通过 zip 把它们合并成一个由表示学生成绩的对象组成的流。

操作符

RxJS 有很多操作符,事实上比创建器还要多一些,但是我们并不需要一一讲解,因为它们中的很大一部分都是函数式编程中的标配,比如 map、reduce、filter 等。有 Java 8 / scala / kotlin 等基础的后端或者用过 underscore/lodash 的前端都可以非常容易地理解它们。

本文重点讲解一些传统方式下没有的或不常用的:

retry – 失败时重试

有些错误是可以通过重试进行恢复的,比如临时性的网络丢包。甚至一些流程的设计还会故意借助重试机制,比如当你发起请求时,如果后端发现你没有登录过,就会给你一个 401 错误,然后你可以完成登录并重新开始整个流程。

retry 操作符就是负责在失败时自动发起重试的,它可以接受一个参数,用来指定最大重试次数。

这里我为什么一直在强调失败时重试呢?因为还有一个操作符负责成功时重试。

repeat – 成功时重试

除了重复的条件之外,repeat 的行为几乎和 retry 一模一样。

repeat 很少会单独用,一般会组合上 delay 操作,以提供暂停时间,否则就容易 DoS 了服务器。

delay – 延迟

这才是真正的 setTimeout 的等价操作。它接受一个毫秒数(图中是 20 毫秒),每当它从输入流中读取一个数据之后,会先等待 20 毫秒,然后再放到输出流中。

可以看到,输入流和输出流内容是完全一样的,只是时机上,输出流中的每个条目都恰好比输入流晚 20 毫秒出现。

toArray – 收集为数组

事实上,你几乎可以把它看做是 from 的逆运算。 from 把数组打散了逐个放进流中,而 toArray 恰好相反,把流中的内容收集到一个数组中 —— 直到这个流结束。

这个操作符几乎总是放在最后一步,因为 RxJS 的各种 operator 本身就可以对流中的数据进行很多类似数组的操作,比如查找最小值、最大值、过滤等。所以通常会先使用各种 operator 对数据流进行处理,等到要脱离 RxJS 的体系时,再转换成数组传出去。

debounceTime – 防抖

在 underscore/lodash 中这是常用函数。 所谓防抖其实就是“等它平静下来”。比如预输入(type ahead)功能,当用户正在快速打字的时候,你没必要立刻去查服务器,否则可能直接让服务器挂了,而应该等用户稍作停顿(平静下来)时再发起查询。

debounceTime 就是这样,你传入一个最小平静时间,在这个时间窗口内连续过来的数据一概被忽略,一旦平静时间超过它,就会往把接收到的下一条数据放到流中。这样消费者就只能看到平静时间超时之后发来的最后一条数据。

switchMap – 切换成另一个流

这可能是相对较难理解的一个 operator。

有时候,我们会希望根据一个立即数发起一个远程查询,并且把这个异步取回的结果放进流中。比如,流中是一些学生的 id,每过来一个 id,你要发起一个 Ajax 请求来根据这个 id 获取这个学生的详情,并且把详情放进输出流中。

注意,这是一个异步操作,所以你没法用普通的 map 来实现,否则映射出来的结果就会是一个个 Observable 对象。

switchMap 就是用来解决这个问题的。它在回调函数中接受从输入流中传来的数据,并转换成一个新的 Observable 对象(新的流,每个流中包括三个值,每个值都等于输入值的十倍),switchMap 会订阅这个 Observable 对象,并把它的值放入输出流中。注意图中竖线的位置 —— 只有当所有新的流都结束时,输出流才会结束。

不知道你有没有注意到这里一个很重要的细节。30 只生成了两个值,而不是我们所预期的三个。这是因为当输入流中的 5 到来时,会切换到以 5 为参数构建出的这个新流(S5),而这时候基于 3 构建的那个流(S3)尚未结束。虽然如此,但是已经没人再订阅 S3 了,因为同一时刻 switchMap 只能订阅一个流。所以,已经没人会再朝着 S3 “叫号”了,它已经被释放了。

规律:operator 打包学

当你掌握了一些基本操作符之后,就可以让自己的操作符知识翻倍了。

这是因为 RxJS 中的很多操作符都遵循着同样的命名模式。比如:

xxxWhen – 满足条件时 xxx

它接受一个 Observable 型参数作为条件流,一旦这个条件流中出现任意数据,则进行 xxx 操作。

retryWhen(notifier$),其中的 notifier$ 就是一个条件流。当输入流出现异常时,就会开始等待 notifier$ 流中出现数据,一旦出现了任何数据(不管是什么值),就会开始执行重试逻辑。

xxxCount – 拿到 n 个数据项时 xxx

它接受一个数字型参数作为阈值,一旦从输入流中取到了 n 个数据,则进行 xxx 操作。

bufferCount(3) 表示每拿到 3 个数据就进行一次 buffer 操作。

这个操作可以看做是 xxxWhen 的语法糖。

xxxTime – 超时后 xxx

它接受一个超时时间作为参数,从输入流中取数据,一旦到达超时时间,则执行 xxx 操作。

比如前面讲过的 debounceTime 其实遵循的就是这种模式。

这个操作可以看做 xxxWhen 的语法糖。

xxxTo – 用立即量代替 Lambda 表达式

它接受一个立即量作为参数,相当于 xxx(()=&gt;value))

比如 mapTo('a') 其实是 map(()=&gt;'a') 的语法糖,也就是说无论输入流中给出的值是什么,我往输出流中放入的都是这个固定的值。

坑与最佳实践

取消订阅

subscribe 之后,你的回调函数就被别人引用了,因此如果不撤销对这个回调函数的引用,那么与它相关的内存就永远不会释放,同时,它仍然会在流中有数据过来时被调用,可能会导致奇怪的 console.log 等意外行为。

因此,必须找到某个时机撤销对这个回调函数的引用。但其实不一定需要那么麻烦。解除对回调函数的引用有两种时机,一种是这个流完成(complete,包括正常结束和异常结束)了,一种是订阅方主动取消。当流完成时,会自动解除全部订阅回调,而所有的有限流都是会自动完成的。只有无尽流才需要特别处理,也就是订阅方要主动取消订阅。

当调用 Observablesubscribe 方法时,会返回一个 Subscription 类型的引用,它实际上是一个订阅凭证。把它保存下来,等恰当的时机调用它的 unsubscribe 方法就可以取消订阅了。比如在 Angular 中,如果你订阅了无尽流,那么就需要把订阅凭证保存在私有变量里,并且在 ngOnDestroy 回调中调用它的 unsubscribe 方法。

类型检查

只要有可能,请尽量使用 TypeScript 来书写 RxJS 程序。由于大量 operator 都会改变流中的数据类型,因此如果靠人力来追踪数据类型的变化既繁琐又容易出错。TypeScript 的类型检查可以给你提供很大的帮助,既省心又安全,而且这两个都是微软家的,搭配使用,风味更佳。

代码风格

如同所有 FP 程序一样,ReactiveX 的代码也应该由一系列小的、单一职责的、无副作用的函数组成。虽然 JavaScript 无法像 Java 中那样对 Lambda 表达式的副作用做出编译期限制,但是仍然要遵循同样的原则,坚持无副作用和数据不变性。

寄语 – 实践出真知

ReactiveX 大家族看似庞大,实则简单 —— 如果你已经有了 Java 8+ / Kotlin / underscore 或 lodash 等函数式基础知识时,新东西就很少了。而当你用过 Rx 大家族中的任何一个成员时,RxJS 对你几乎是免费的,反之也一样。

唯一的问题,就是找机会实践,并体会 FRP 风格的独特之处,获得那些超乎具体技术之上的真知灼见。


更多精彩洞见,请关注微信公众号:ThoughtWorks洞见

Share

ThoughtWorks的专业精神

我们说ThoughtWorks是一家专业服务公司(Professional Service Firm),专业服务是一种业务模式,早期通常指的是医生、律师、会计等,后来管理咨询顾问、IT或生物等领域的科技顾问也加入了这个行列。专业服务的特点是:高度知识密集,专业从业人员需要长期积累经验并持续成长,通常是以帮人解决问题来创造价值。

当我们提到“专业”的时候,通常是相对于“业余”来说的。专业区别于业余,是在于对专业发展的长年持续投入,以及在此基础上建立起来的在相关知识领域的权威性。

ThoughtWorks的权威来自对卓越的追求。过去,ThoughtWorker以“在代码级对设计和质量的追求,对新兴技术的快速掌握,以及我们解决工程技术问题的能力”,让我们在软件工程实践领域成为业界的翘楚。未来,是否能在更大的广度和深度范围里进一步取得卓越的效果,则取决于我们能力的发展。

专业能力的发展一方面是在知识、技能的持续更新;另一方面是通过刻意的锻炼和实践,持续提升专业所需的素养,或者也叫胜任力。我们以往常把专业知识和胜任力合在一起看,现在,我们在新的Competency Model里定义了Craft Skill和Amplifier,分别探寻个人在其方向的发展方式。

可能不少同事已经听说,我们开始在Lead Consultant范围开始试点和推进新的能力模型,这个试点的重点是Amplifier。虽然每个人对各种能力的提升有着自己的偏好,不过囿于自己的文化、业务,以及相对应的战略,我们能力的发展也凸显着ThoughtWorks的特质。以能力模型里的Core Competency为例。

Self-Confidence(自信)

“自信”使得ThoughtWorker总能够突破自身现有水平,迎难而上,能够做别人不敢做,做不到的事情。在公司久的人可能听到过一些故事,比如在新的技术领域,跟客户团队同时起步的情况下,我们的咨询师总能领先一步,趟过客户难以解决的问题,进而辅导客户的团队。在客户面前,我们ThoughtWorker总能积极寻求合适的时机,挺身而出,表达我们的主张。跟小伙伴一起,我们总能真诚地赞扬ThoughtWorker出色的行为,同样,我们也应该勇敢坦诚地给身边同事反馈可以改进的地方,互相帮助成长。

Developing Others(发展他人)

一位ThoughtWorker到了新的团队、新的办公室,甚至出差到另一个国家,只要一进办公室, 那熟悉温暖的感觉不仅仅来自公司的Logo,更来自第一时间感受到的周围同事热情的支持、鼓励和无私的分享和帮助。不过发展他人不仅仅是在看到需要的时候提供帮助,每个ThoughtWorker都有责任,都要具备能力,发现同伴的不足并提出反馈,通过安排合适的锻炼机会,鼓励同伴突破舒适区,掌握新的能力。

Delivering Customer Results (客户成果交付)

直接的或间接的,客户价值也是ThoughWorker价值的体现。我们在专业上确实会有自己的追求和兴趣,但是在项目上,我们的设计,我们的分析,我们的代码,以及我们的各种决定,如果不能产生客户价值,不能帮助客户成功,那都是耍流氓。

Technical Expertise(技术专长)

如果说几乎所有的公司都对员工有技术掌握程度的要求,我们在跟很多客户合作过程中发现,ThoughtWorker与其它公司的技术从业者相比,我们具备更加广泛的视野,我们更能够坚持探索和推动正确高效的问题解决办法,而对业界最新进展的全面深刻的理解是我们超越业界同侪的标志。

是不是有了超强技能就够了?我们有的时候会对某人作出直观地判断 ——“这人看上去很不专业”。这种判断可能并不是来自这个人解决问题时的表现,而是来自于对其态度和行为的观察。有责任的态度和行为体现在两个层面。一个是个人对客户层面,一个是组织对社会层面。

个人和客户层面上, 拥有自己行业的行为操守(Code of Conduct)通常是专业化的一个特征。在最早的几个专业服务领域,比如医生、律师、会计等,对道德规范都制定有强制性的守则,这在注册认证中都是必考的内容。如果被发现有违反行为,就会吊销相关注册。作为面向客户的咨询师,我们依据业界的实践,针对公司的核心价值,制定了专业操守相关的规范 ——《ThoughtWorks员工行为准则》。

当然我们不能满足于只是遵循这几条基本的要求,还要做到一些更能体现我们专业范儿的行为。我们面对挑战的环境要展现自信,却不需要表现得居高临下,显得自以为高人一等。跟客户和团队内外小伙伴沟通的时候,应及时同步更新,不要轻易假设某些信息跟他们没有关系,全面提供上下文和自己做出判断的背景信息,不要对他人提出问题和挑战有什么负面的情绪。

对于要推动的事情,我们既然很难全面考虑客户的立场,那么不妨多给几个选项,看看对方是如何权衡。客户是有血有肉有诉求的个体,他们找到我们并不是因为人傻钱多,我们的服务也不仅仅是完成一个个合同要求的交付物,就像我们使用设计精良的产品时一样,客户跟我们合作的体验应该是充满兴奋和激情的。即使做不到兴奋和激情,至少也要痛并快乐着。

展现这些行为都很不容易,特别是还有很多其它压力的时候,需要大量的刻意练习和不断的反思,才能慢慢练就。但如果是随便什么人都能做到,那还叫专业吗?

专业还意味着对于这个社会所承担的道德贡献。我们印象中只关心数字准确与否的注册会计师,在这个群体的道德规范里明确说明,他们的服务对象不仅仅是企业,而是包含社会各方“所有与企业有关并关心企业的人士,可泛指为社会公众”。注册会计的信誉实际是要为社会经济生活中至关重要的市场信誉兜底。会计师是如此,律师这样对社会个体是否能获得公正待遇,以及医生这样与个人生命健康息息相关的专业,在专业技能之上的道德目标就显得愈发重要。

那么ThoughtWorks作为有志成为社会模范的公司,把社会和经济的公正列为了三个支柱之一。跟很多在讲社会责任的公司不同,我们更把对社会的影响当做目的而非手段。这也是为什么我们在各个城市Office举办开拓眼界的session,组织”你我同行”这样的体验活动,积极跟各种社会组织建立合作关系。我们特别希望更多的ThoughtWorker在P3领域能够经历从知道,以至理解,最后到行动的旅程。

MD心声

提升TW的Professionalism水准有什么合适的方法?Professionalism是个很大的话题,不过还是渴望想听到从不同角度的思考。


推荐阅读

Share

准备好开启你的领导力之路了吗?

都说不想当将军的士兵不是好士兵,相信所有不愿意昏庸度日,有理想、有上进心的人都渴望成为一个将军吧。但你准备好去做一个将军了吗?你知道怎样才算是合格的将军吗?你知道如何成为一个优秀的将军吗?

成为将军后,你又该如何去面对即将遇到的挑战呢?归根结底,就是如何发展领导力,顺利完成角色的转变,成为一个好的领导。

发展领导力

究竟什么是领导力,它和知识、技能有什么区别呢?领导力实际上是一种能力,更多指的是一些软技能,具有可迁移性,适用于任何岗位;不只局限于工作,相同的能力在生活中同样适用。如何知道一个人是否有能力,需要结合他的行为来看。面试中有一门方法叫做行为面试法,“根据候选人过去实际行为的事例描述来评测其胜任力,通过收集候选人提供的行为事例来预测候选人未来将会如何处理相关情况”。

角色转变与角色认知

我们有时会遇到这样的场景,领导抱怨“某某员工格局太小,视野太局限,不能从大局出发,站在自己的视角上看待问题,处理问题不够全面“。其实仔细想想,出现这样的情况很正常。处在不同的位置上,接触的人、看到的风景、处理事情的复杂度自然是不同的,当经历过许多挑战、克服过很多困难,随着经验、能力的不断提升,自然能够很好的处理一切。但是对于员工来说,他们的角色、位置、经验、资历决定了他们没有你这样的环境、没有经历过你所经历的挫折困难,没有和你相同的机会,如何能够期盼他们能成为像你一样的人,像你一样全面的考虑问题、处理事情呢?

在工作中我们的关注点更多的是业绩,业绩实际上是做出来的,也就是说是由行为产生的,行为的产生是需要特定环境的(不同的环境产生的行为是不一样的),也就是说如果你希望这个员工能够有这样的行为,你首先得给他这个机会,创造这样的环境。只有机会当然是不够的,能够产生这个行为少不了知识和技能的支撑。除了这些客观因素,还得考虑个人的想法,什么事情对他来说是最重要的 ,他愿不愿意去承担这个责任。

大多数人在角色转变的过程中会体会到各种不适,例如从技术工作者转变成管理者的时候,会很排斥那些沟通、管理工作,认为这些琐碎的事情占据了自己的大部分时光,非常怀念之前能够专注研究技术、写代码的时光,工作的很不顺心。产生这样结果的根因就是他的角色和角色认知是分离的。他的角色是一个领导,需要完成作为领导应该尽的责任,但是在他的认知中自己依旧是一个技术人员,更加喜欢花时间去专研各种前沿的技术。当角色和角色认知冲突的时候,工作自然就不会顺利。这个时候就需要一个自上而下的自我反思了。知道什么对于作为领导最为重要、知道应该学习哪些知识和技能让自己能够胜任这个新的角色、知道作为一个领导在什么样的场合应该做什么,这才是对角色的正确认知。当然,在这个过程中不可避免会失去一些,例如那些无人打扰的时光,毕竟有得必有失,有舍才有得。

(角色认知模型) ​

管理者角色

我们常见的管理者角色有以下几种类型

  • Expert:某一方面技术能力很强,是某个领域的专家
  • Manager:擅长发布任务,设定目标,保证目标的达成
  • Coach: 具有发展他人、团队的能力
  • Leader:知道如何用正确的方式达成目标,激励人

一个领导往往包含其中的一种或是几种特质。前两种主要在于做事,后两种主要在于管理人。这几种类型的领导都会有自己的局限。

  • Expert:可能因为自身出色的能力,技术专长吸引到许多员工,但是潜在问题是,可能员工学会了就会离开。
  • Manager:用你的职权去领导员工,虽然简单,但是不会长久。
  • Coach:也许会因为你能够帮忙发现他的不足,帮助他进步留下来。
  • Leader:因为和你一起工作很开心才会留下来

那么你可能会问“到底什么样的领导才是好的领导,我到底要戴上面哪几顶帽子呢?”。这个自然是因人而异,戴什么样的帽子取决你想成为什么样的领导。可以结合自己的特点、专长,辅以角色认知模型,确定你自己的领导风格。

管理挑战

从个人贡献者转变成领导者,自然而然会遇到许多的管理挑战:

  • 如何提升组员的能力
  • ​如何精准的判断出员工的强弱项
  • 如何了解员工的发展期望
  • 如何在沟通中引导出员工的真实想法
  • 如何激励员工
  • 如何树立影响力
  • 如何确定管理底线、管理选择
  • ……

想要克服这些管理挑战,需要我们提升多方面的能力,例如沟通技巧,能够知人善用,能够辅导员工,发现他们的不足并帮助提升,能够持续不断的给员工反馈,让它成为工作中的一部分。

管理始于沟通

想要做好管理,好的沟通技能必不可少。沟通是信息的双向流动,是思想、感情的传递与反馈。我们工作生活中遇到的很多挫折都是因为沟通不到位导致的,那么常见的沟通问题有哪些?

认知偏差

看同一幅画,大家看到的画面是不一样;品味同一篇文章,得到的体会是不一样;吃同一道食物,味觉的刺激也是不一样的。当站的角度不同,关注点不一样的时候,看到的场景自然不一样,这并没有什么对错之分。因为存在视觉盲点,看到的世界有一部分是脑补出来的。一旦发生分歧的时候,人们通常并不会意识到可能是认知偏差导致的,反而会下意识的维护自己、反驳别人。

(图片来自:http://t.cn/Ewsq9BE)

世界是客观的,认知的世界是主观的,人和人互动实际上是两个主观世界的互动。那么如何克服认知偏差呢?

  • 包容心,你的包容度和你能管理多少员工成正比。要认识到人与人之间没有好坏,只有不同
  • 当改变不了别人的时候,只能改变自己

那究竟什么是有效的沟通呢?总结以往的经验,好的沟通总是能够达到沟通的目的,沟通的过程中参与人员能够做到仔细倾听、良好互动、彼此尊重,无效的沟通刚好相反。

通过上述信息,会发现人际互动是有两种需求的

  1. 个人需求(心情):每个人在工作和互动式的“人性”的需求
  2. 实际需求(事情):希望通过互动实现的目标

想要做有效的沟通,你需要满足这两种需求,既关注人,也要落实到事。

沟通障碍

产生沟通障碍最常见原因是:听不到位、缺乏参与、无效表达。

1.听不到位

沟通达不到预期效果最重要原因之一就是听不到位。要么是没有认真听,错失重要的信息;要么是因为认知偏差没有理解对方说的意思,那么结果自然达不到预期;要么就是只听了事情本身,没有听到对应的情绪、心情,这样可能事情依然可以完成,但是长此以往不利于团队的管理。

聆听小秘诀:

  • C(Concentrate):专注集中,尊重对方的发言
  • A(Acknowledge):以自己的语言复述对方所表达的意思;在复述后通过澄清与对方确认理解(例如:你刚才提到了xxx,你的意思是xxx, 是这样的吗?)
  • R(React) :回应情绪。聆听,除了关注事件本身,也应该关注讲述人的情绪,满足人际互动的两种需要,保持同理心。同理 != 同情/同意/比惨/给建议/搞调查。同理 = 回应事实(我观察到你最近经常加班)+ 表达感受(你一定会觉得很累)

2.缺乏参与

另外一种常见的沟通障碍就是缺乏参与,沟通是信息的双向流动,需要有思想的碰撞,缺乏互动的沟通只能算是单方面通知。如果以下命令的方式或是参与方被动参与,很难达到预期沟通的效果。那么如何提高参与性呢?答案就是提问,通过提问能够收集信息、了解真相;澄清疑虑、核对想法;拓展思维、鼓励参与。

“下君-尽己之力,中君-尽人之力,上君-尽人之智” — 韩非子

作为管理者就要做到能够集众人的智慧解决问题,提问就是收集信息,促进思考最好的方式之一。

问题的两大类型:

  • 封闭式问题(是或者否或者只有几种固定的回答)
  • 开放式问题

用开放式问题提问能够激发参与者思考。

  • WHAT(你的困难是什么?可能遇到的困难/挑战/障碍?)
  • WHY(什么原因你会这样建议?你这样建议的理由?)
  • HOW(你打算如何开始/进行?要怎么做?下一步要怎么做才能有效的面对现状?)
  • WHEN(你认为什么时候可以完成?我们合适开始进行?)
  • WHO(谁可以帮助?哪些人可以进入这个项目计划?可以从谁那里获取资源/协助?)

当然提问也需要 有一些注意事项,例如问题不要过于复杂、尽量简单;不要过于发散,运用跟进式问题;要尊重对方,有敏锐度,不要问隐私性问题;问问题不要尖锐、带强迫性,不要激发对方的防御心。

3.无效表达

另外一个非常重要的沟通技巧就是“说”,你可能不以为然,说话谁不会说呢?但是很多沟通不到位的原因其实是说不到位。说是一个信息传达、坦诚分享、建立信任的过程。仅仅是提出意见、建议是不够的,还要适度的分享这么做的原因、理由,自己的感受、想法。如果只是提出意见,不告诉这么做的理由,大家可能会自行脑补为什么你要这么做,那么认知偏差就产生了。

破除常见的沟通障碍,那我们来看看一个好的沟通、互动流程是什么样的。

  • 定:定方向, 明确沟通的目的以及重要性,为什么会有这次沟通谈话
  • 理:理情况,理清当前的事实,有哪些问题、疑虑,相互交换信息
  • 想:想方案,针对当前的问题有哪些解决方案,需要什么样的支持,需要哪些资源等等
  • 明:明做法,制定出行动计划,如何进行后续的追踪,发生变化如何应对
  • 做:做总结,总结这次沟通的要点,给予 信心/鼓励

分派任务

作为团队管理者,不代表要事事亲为,如果这样做了,那么只会焦头烂额。分派任务、授权就变得至关重要。

在分派任务前需要明确这次任务期待的结果是什么,花费的成本、预计完成的时间、验收的条件等等。同时需要选择合适的人选。这个需要结合两方面考虑,一是客观条件:完成这个任务需要的知识、经验、能力;工作量的多少;任务的重要、紧急程度。二是个人意愿:这个人所具备的知识、经验、能力;他个人的发展意愿、兴趣点;他是否有时间,如果交给他有什么风险,有什么地方不合适。综合两方面考虑选择最合适的人选,“把最合适的人放在合适的位置上”,然而现实中是很难实现的。

最常见的问题有以下几种:

  • 不会做:候选人缺乏知识/经验/技术/能力
  • 不能做:候选人缺乏资源/时间/授权/精力
  • 不愿做:候选人缺乏意愿/动机/信息/兴趣

这就需要具体问题具体分析了。不会做那我们给他创造尽可能多的机会,让他快速的学会 所需的知识,技能;不能做我们可以帮助他调配资源,协调他手头上的工作任务的优先级;不愿意做可以去沟通了解他的真实想法,给他激励。

同样定、 理、想、明、做也适用于指责分工互动流程。

  • 定方向:要分配什么任务给对方,为什么要做这个工作,为什么选择他做这个工作
  • 理情况:说明任务指责,时间要求,期待的结果;了解对方对任务的担心和顾虑
  • 想方案:如何解决对方的担心、顾虑;提供什么样的建议
  • 明做法:明确后续的行动方案、步骤;确定所需要的资源;如何追踪、跟进、衡量任务的进展
  • 做总结:总结讨论的要点,确认对方的信息,获取对方的承诺。 许多授权失败的案例都是源于没有进行有效的追踪。

“人们不会做你希望的,只会做你检查的。如果你强调什么,你就检查什么。你不检查 就等于不重视” —郭士纳。

运用有效的追踪方法,利用现成的多种渠道,确定追踪的频率并且根据情况实时的调整,鼓励对方主动承担任务的进展汇报、反馈工作,这样才可以保质保量确保任务的顺利进行。

管理精于辅导

管理团队实际上是一个不断发现问题、解决问题的过程。成功型领导能够防患于未然,在关键的时间节点未雨绸缪将问题扼杀在摇篮里。但更多的是改进型领导,在团队出现问题或是发现征兆的时候,及时介入,辅导员工,解决问题,避免更大的影响。

有两类常见的问题,一类是工作成果不佳:工作结果在质量、数量、准时率或是成本控制上面表现的低于要求标准;另一类是不良的工作方法或是习惯对员工或是团队的绩效、士气产生了负面的影响。

诊断问题的流程:

1.发现征兆

作为管理者发现一些不好的征兆(绩效不佳、行为不良、能力不足、动力不高、态度不对),一定不要轻易下结论,提防陷阱,避免误判,常见的陷阱:

  • 过多假设:没有事实根据,凭借猜测作出各种假设。例如:某个同事最近心情不佳,猜测他是不是和其他同事闹矛盾了,可能实际情况是是他的家里孩子生病了。
  • 严苛效应:要求过高,过于严苛,最常见的场景就是:某某人这么简单的事情都做不好,要是我的要半个小时就做完了。以要求自己的程度要求别人
  • 近期效应:最近表现不佳,就认为他一直表现不好
  • 以偏概全:某个人对他有不好的反馈,就认为他的能力有问题

2.收集信息

避免自己掉入到陷阱,对其他人作出误判,需要从多元管道收集信息,了解当事人的工作成果、行为展现,还有哪些问题有疑义、有待澄清。当然收集信息的渠道需要尽可能多,例如获取各种统计报表、核查记录,书面记录, 获取主管、同事、客户等等人的反馈,获取当事人自我评价记录,尽可能的全面。而且对当事人行为描述反馈要基于事实,运用STAR。

3.检验原因

运用有效的分析工具方法找出问题点,找出问题的关键原因,例如是当事人不会做、不能做还是不想做。

4.做出判断

找出问题的多重原因之后,选择优先级最高、影响最大的优先解决,找出可能的解决方案,准备和当事人的沟通方案。

管理成于反馈

反馈在工作中至关重要,除了能够增加与员工的互动,提升信任度,更能够帮助员工正确的认识自己,了解自己的优势与劣势。

有效的反馈需要具备一下几点:

  • 及时:反馈一定要及时,这样便于当事人及时作出改进;如果当事人不认可也可以及时分析问题原因,减少误解
  • 平衡:不要只给负面的反馈,正面的反馈的影响同样也是巨大的,正向的行为需要不断的强化,这是激励他人的一个好的方式。负面的反馈帮助员工发现自己的不足,不断提升自己
  • 明确:反馈一定要是基于事实的,真诚而具体的,不要泛泛而谈

给予反馈的过程中需要仔细聆听对方的看法、确保理解,需要尊重肯定对方,而不是持批判的态度,如果能够适当的给予一些建议更好。

总结

无论是已经行进在领导力之路上,还是即将开启领导力之路,从现在开始反思自己的角色认知模型,从内心深处认识到你究竟想成为一个什么样的领导,不断朝着目标前进。

了解沟通的重要性,承认认知偏差,破除常见的沟通障碍,满足人际互动的两种需要,完成有效的沟通。

知人善用,给员工创造成长、进步的机会。当发现团队存在问题的时候,谨慎处理,不要步入陷阱、提防误判,作出有效的辅导。

了解反馈的力量,让它成为工作中必不可少的一部分。当你具备这些能力的时候,你会发现它不仅影响了你的工作,将它迁移到生活中,也同样适用。


更多精彩洞见,请关注微信公众号:思特沃克

Share