高阶的编程模型
通过一系列「优雅的 Mac」的讨论,我提到这些快捷的本质是为你更快地处理尽可能多的「背景事务」,以使你能够专注在「更重要的事情」上。更重要的事情是什么呢?产出价值。产出代码。这里,我们暂且假定 代码=价值
,那么有两个推论:
- 如果「价值」本身有问题,那么整个所谓效率体系,都没意义,因为没有产出有用的价值。这个问题,我会在效率集里讨论
- 如果你不产出代码,那么你没必要节约其他部分的时间,因为你就没要完成的东西啊
更遑论,产出的代码本身不一定就100%达到了你要的价值。在业务代码的上下文下,你产出了代码,解决了问题,就是为公司产出了价值。在自己做产品时,写的代码有没有价值,就难说了。因此,更快的产出,才能支持更快的验证、反馈,甚至抛弃,才能作为整个敏捷体系的支撑。代码产出越慢,价值反馈越慢,你能试错的机会越少,机会成本越高。
骨架代码
这个 issue 要谈的是「产出代码」这个事情本身。我发现,即便是在产出代码,你在写的很大一部分代码也不一定是产生了直接价值的代码,比如框架代码,比如基本的控制结构。我把你要写的代码想像成一篇文章,这篇文章应该是有它更高层次的结构的。并且,这个结构与你所写文章的类型——理解为你所用的框架——有直接的关系。打个地方:
- 一个 React+ES6 的组件,首先一定是一个类,它必须有一个
render
方法,可能有若干生命周期方法。这些是它的结构、骨架,有了骨架,你再往里填东西就是了 - 一个 Java 服务, 首先它也一定是一个类,必然是这样的结构:
class MyClass { ... }
。里面可能有若干方法。这些是它的结构、骨架,有了骨架,再往方法里填东西就是了 - 一个用 chai+mocha 写成的 JS 单元测试。首先它一定具有
describe('', () => { it('', () => {}) })
这样 的骨架,一个测试里,一定有given
(准备数据)、when
(调用方法)、then
(验证返回)三个部分。有了骨架,你再往方法里填东西就是了
可以看到,这些代码,都不一定是有价值的代码。你所写真正的代码,应该是那些骨架里的函数、方法里的那些代码。骨架代码,生成就行了。这是我要提的第一个点,骨架代码生成。说白了,就是这样一个 API:
generateSkeletonCode(...techStack)
基本控制结构
好,我们真正要写的就是那些骨架里的函数方法代码。但是莫急!这些函数里面,依然不需要你去写的所有的代码,我们还有更为基本的组件。那,就是我们经典的三大编程结构:statement
、if-else
、while
。顺序结构、条件结构、循环结构。各种结构有变体,不妨碍使用,而且为了减少记忆负担,尽量只用一种可应对万能情况的结构。
细细拆分下来,顺序结构里面,一般是 变量计算、API 调用 与 赋值 等,本质都是通过计算和请求获得可供下步计算或返回的 数据;循环结构,其实在 JS 这种特定语言里,又可以使用函数式编程来代替。所以,我们在实现函数或代码的时候,可用的编程结构分列如下:
- 顺序结构
statement
- 变量计算。通常无特定模块,手敲即可
- API 调用。这个又是一个子模块,它又根据你所用的技术栈不同而有不同的语法(如你用
$.ajax()
、fetch()
不同的 API 语法不同,你用 HTTP 请求与用 FTP 请求需要的协议参数又可能不同),但它又有共通的接口,如都需要你传入url
、settings(optional)
、回调等。其实又有一个编程结构的 API:generateAPICode(...techStack, url, settings, params, successCallback, errorCallback)
- 赋值。上两步都是为了取得数据供后续计算,那么数据就得保存起来,会发生一次赋值操作。这个在 Intellij 里已经有后向声明这样的特性支持
- 条件结构
if-else
switch
。generateConditionComponent(condition, ...opeartions)
。这个 Intellij 里也已有if
这样的 live template - 循环结构
while
。generateLoopComponent(startCondition, stopCondition, accumulateCondition, operations)
。对于数组来说,这样的循环则可进一步简化为高阶函数式编程,现已有完美支持:array.map()
、array.filter()
、array.reduce()
、array.find()
、array.any()
等
理解了这些,其实编程产生的直接价值,真正需要你的东西,除了把代码写好让人容易理解到极致之外,就只是输入数据了。我们的工作,其实就是运用调配这些框架代码、编程结构,从原始的输入数据处,产生正确的输出。如是而已。中间调配过程,能越快完成越好。
实践落地
说了那么多,怎么落地呢!?
Mindset
思维转换。比如要 TDD,就要先把思维切换到 任务分解、快速反馈验证 这样的工作论上来;这种编程模型,本质是把代码做从上至下的组件划分,从框架性的最高层组件,到下面的结构组件,通过分离组件模板代码和真正需要我们编写的代码,尽量剔除无用的工作。新的思维模型会出来,正是因为以往直白的直觉思维,已经无法高效应对更复杂的问题。因此,尝试、辩证、质询看待一种新的思维方式,才是成长式的思维嘛。
真的要落地了
- IDE 支持:WebStorm 插件?
- 没了?
自己撸一个。核心代码尽量做成可复用的 API,这样不同的编辑器插件只需依照不同的模板集成就行了。就像 JUnit 5 分离出一个 jupital-core 的核心包出来一样,以后即可适应 gradle runner, maven runner, Intellij plugin, Eclipse plugin 等等不同的三方插件了。
API
举个非常简单的:
1 import React from 'react' 2 3 class **TodoApp** extends React.Component { 4 static propTypes = { 5 **todos**: React.PropTypes.string 6 } 7 8 render() { 9 return ( 10 **<ul>** 11 **{ todos.map(todo => <li>{ todo.name }</li>) }** 12 **</ul>** 13 ) 14 } 15 } 16
这个16行的 React 类里,其实真正需要我们写的,只有打了星号的部分,仅3行 + 2个变量名,也就是 类名、传入参数、组件 render
方法真正要做的事,其他的什么 class xxx extends React.Component
、render()
等都是 ES6+React这个技术栈本身所需要的骨架结构,完全可以一键生成;map
是实现过程中使用到的循环结构;html 这些,甚至可以用 emmet 这样的工具插件来完成。emmet 这个工具其实也是一样的思想,HTML 的标签都是模板,尽量生成就好,手撸个啥,HTML 页面真正需要你写的,就是把传入的数据展示出来,如是而已。
按 实际产出 / (实际产出 + 框架代码 + 基本编程结构)
这个公式来看,写一个16行的类,不产生直接价值、可通过模板生成的代码有75%。
好在,在 React 上,现在真的有 Intellij 插件完成这个事情了。剩下我们自己真正做的,真的就只有对输入的处理逻辑,这是我们最应该专注的地方。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论