在编程中,“上下文”这个词通常指的是程序执行的环境和相关状态信息。本文记录相关内容。
上下文(context):位于关键词前部或后部的词句或文字。它是关键词所处的语言环境,影响关键词的含义。通过阅读上下文,有助于理解和鉴别某关键词所具有的准确含义和用法,判断文献是否相关。
—《图书馆·情报与文献学名词》
上下文切换(context switch):根据某种条件,暂停当前进程或线程的执行,保护当前进程或线程的现场,恢复另一个进程或线程的现场,转而执行该进程或线程的过程。
—《计算机科学技术名词 》 (第三版)
编程上下文共有三个类别。
譬如说:某个后台系统,登录实体为个人,所以功能都是以个人为基准来触发的。但有一天,业务需求,集体账号也可以登录,该账号登录后要获取到集体所有个人的信息,那在触发功能的时候,原先以个人为基准触发的功能就需要兼容到集体账号。这个过程就是的业务上下文变更的操作。
业务上下文笼统的说就是业务操作所对应的基础单位(实体),那如果业务上下文需要变更,那需要更改的代码以及需要做的回归测试是很庞杂的。所以业务上下文变更需要慎之又慎。
大家应该对一句话有印象:(进程/线程)上下文切换开销很大。这里的上下文一般指的就是运行程序的上下文。
一个应用程序有可能由多个不同的编程语言文件组成。
比如web网页,由html,css,js文件组成,每个编程文件都有自己的上下文,所以要如何将html中的数据传递到js文件中呢?这便是 data-*
元素属性的目的了。
data-*
全局属性 是一类被称为自定义数据属性的属性,它赋予我们在所有 HTML 元素上嵌入自定义数据属性的能力,并可以通过脚本在 HTML 与 DOM 表现之间进行 专有数据的交换 。 通过添加data-*
属性,即使是普通的 HTML 元素也能变成相当复杂且强大的编程对象。
又比如原生应用的webview页面时,会处理webview页面和原生应用之间的通信问题。一般地,都是原生应用在window对象上挂载一系列方法,JS代码可以通过调用这些方法从而触发原生应用的功能。该过程通信慢,响应不够快速。所以社区产出了react-native模块来优化通信问题。这里的通信问题就是进程上下文切换开销大的缘故。
方法上下文可以理解为方法所在的作用域。类亦如此。
还有一些比较特殊的概念实际上也是和方法上下文相关。比如worker语言特性中,需要监听message事件,这实质上,就是在当前上下文监听另一个上下文的状态。再比如回调函数。实际上是因为上下文变更后,对原有上下文访问的一种编程手段。从这个角度看,闭包实质上也是对上下文的一种操作手段:返回一个函数,该函数在当前上下文可以操作闭包上的上下文。
具体来说,上下文可以有以下几个层面的含义:
this
的值。其实我们编写程序,大概都是这个的范式:
在做决策的步骤中,一个关键的步骤就是根据足够多的信息做决策。所有必要的信息统称为【上下文】。
在实际编码过程中,上下文的存在有很多具体的形式。比如全局变量,ThreadLocal,函数参数等。
如果一段逻辑很简单,函数参数就足够用了。比如
123 | User getUserByID(int userID) { return repo.Get(userID);} |
---|
这里userID是一个参数,repo是一个全局可访问的代表db的对象。(在Java里,因为不支持真正的全局变量,一般会被注入到当前的Bean里,但这不影响讨论。)
但逻辑复杂之后,这些参数就不一定够用了。比如你有如下的options。
如果还简单用函数参数列表写,可能就变成这样了:
12345678910111213 | User getUserByID( int userID, // 获取什么用户 int opUserID, // 哪个用户触发了这次的动作,用于feature gating String env, // 当前是啥部署环境 boolean needName, // 是否要返回用户名(并解密) boolean includeDeleted, // 是否返回已经删除的数据 ClientInfo client, // 啥客户端发的请求,版本号和平台信息 boolean disableCache, // 是否禁用cache boolean enforceUseMaster, // 强制用主库 int failRate, // 错误概率 ...) { // 无比复杂的逻辑 } |
---|
这代码就没法看了。
这些条件都写函数参数就会让函数参数列表变的很长,因此有一些办法。比如:
我们常说的DDD,设计领域对象,实际上的意思就是说要利用对象来构造一套上下文,支撑相关的所有业务逻辑。
比方说,有一个复杂的推荐逻辑。用户明确的参数就只有一个关键字,要求返回和这个关键字相关的产品。编写一个对象可以维护:
为此就可以编写一个XXXSearcher的class来串联整套逻辑。如果逻辑足够复杂,这个class就可以进一步升级为一个微服务或者一个中台。
因此很多人理解的“面向对象”,除去messaging的那一面,我理解还有一方面的实际上就是以更好的形式去组织业务逻辑的上下文,将业务的复杂性表述清楚,最终指导代码的执行。所以对象内搞定业务逻辑上下文;对象间用messaging通讯(或者说用最简单直接的API通讯)。
大部分的业务开发花最多时间的就是在编写代码去收集这些上下文,但往往最不上心的就是好好组织和管理这些上下文。复杂业务的上下文收集起来成本高昂,但很多人却会忽视他们的重要性,不好好写清楚注释和文档,提高他们的复用性。着实可惜。
—— 知乎