我正在参加「启航方案」

前文中咱们用数据转化的思维,手把手地对事务需求进行了拆解,简略得令人惊讶!毫无争议,这是数据转化思维的劳绩!

咱们也讲了,依照一贯的惯例办法,在数据相关与打通上,咱们无能为力,无法发挥数据转化思维(原因请回忆前文)。可是看看事务需求拆解,获得了实实在在的优点,咱们又迫切希望数据转化思维,能够在数据相关与打通上大展拳脚。

既然惯例办法不可,那咱们就走出一条新的光亮之路来。让咱们拨去蒙蔽双眼的惯例旧思维,用清澈明达的双眼,好好看看这已到面前的光亮。

让我来告知你怎么办。

先从一个小小的示例开端(先睹为快)

咱们运用面向未来的 rainforest-js 来实现这看似不可能完成的使命。

先别多想,只是看,稍后会解说。

Step.1

const MyStruct = typedef({
    fieldA: string,
    fieldB: string,
    fieldC: string,
})

看这个数据结构,依照数据转化的思维,咱们应该令 fieldA>fieldB>fieldC,来看看怎么做。

Step.2

ruledef(
    MyStruct,
    'rule1',
    {
        fieldA: true,
    },
    (self) => {
        self.fieldB = self.fieldA + '.org'
    }
)
ruledef(
    MyStruct,
    'rule2',
    {
        fieldB: true,
    },
    (self) => {
        self.fieldC = self.fieldB + '.cn'
    }
)

咱们界说了两个规矩,rule1 用来将 fieldA 转化为 fieldBrule2 用来将 fieldB 转化为 fieldC。接下来,咱们用真实数据验证下。

Step.3

const myself = typeinit(MyStruct)
myself.fieldA = 'rainforest-js'
console.log(myself.fieldB) // rainforest-js.org
console.log(myself.fieldC) // rainforest-js.org.cn

完美,数据转化都是正确的。

一个合理的解说

Step.1

咱们界说了一个数据结构的类型描绘 MyStruct,包含三个字段。

Step.2

依据需求 fieldA>fieldB>fieldC,咱们这样来考虑:

  • fieldA 是最初始的输入,应该由用户输入。
  • fieldB 是从 fieldA 转化而来的,所以,咱们就为类型描绘 MyStruct 界说一个规矩。规矩的行为是:当 fieldA 发生改变时,自动将其转化为 fieldB
  • fieldC 同上。(发散考虑:字段更多会怎么?

规矩,从姓名上,你就能感受到它的强壮、强制、不可违反,必定不可能犯错!

咱们再来考虑下,既然是规矩,那么规矩的触发,是必定不需求用户自动控制去触发的。就像,你不需求自动触发万有引力,它一直都在,不可违反。

所以,咱们只是在 rule1 中描绘了:当 fieldA 发生改变时,将其转化为 fieldB。咱们严厉践行了数据转化思维,表达清楚了数据:从哪里来,到哪里去。

Step.3

依据类型描绘 MyStruct,初始化了一个实例 myself,然后为字段 fieldA 赋值,终究打印出希望的成果。

fieldBfieldC 的值是自动转化来的,咱们什么也没做,没有自动控制,仅仅是为 fieldA 赋值了最初始的输入数据。

就这?当然不!

上述的解说连皮裘都算不上,只是让你初窥数据转化思维的一丢丢影子,在脑海中先有个轮廓,大概能看得懂代码就行了。

不过,上述虽然简略,但却道出了数据转化思维编程的中心规律。值得时常回味,刻入魂灵。

另一个示例

咱们略微变一变,引入子结构体后,看看是怎样的。

const OtherStruct = typedef({
    fieldA: string,
    /* ... */
})
const MyStruct = typedef({
    other: OtherStruct,
    result: string,
})

然后在最外层上界说规矩。(为什么不在内层结构体上呢?

ruledef(
    MyStruct,
    'rule3',
    {
        other: {
            fieldA: true,
        }
    },
    (self) => {
        self.result = self.other.fieldA.toUpperCase()
    }
)

接下来,验证成果。

const myself = typeinit(MyStruct)
myself.other.fieldA = 'rainforest-js'
console.log(myself.result) // RAINFOREST-JS

咱们再变一下。

const myself = typeinit(MyStruct)
myself.other = typeinit(OtherStruct, {
    fieldA: 'rainforest-js',
})
console.log(myself.result) // RAINFOREST-JS

成果也是正常的。为什么呢?
很简略,由于 other 整个都变了,那么 other.fieldA 必定也发生改变了。

咱们持续变。将 other 赋值为空。

const myself = typeinit(MyStruct)
myself.other = null

猜猜会发生什么?会报错抛出异常吗?
答案是:什么也不会发生,也不会报错。
由于,规矩 rule3 里明确地描绘了需求调查的是 other.fieldA,而 other(null) 是必定不包含 fieldA 的。所以,规矩 rule3 是不会履行的。(由于不符合规矩呀,对不对。)

这样,就赋予了咱们一种轻松的能力,只需在界说规矩时,描绘清楚待调查的子结构字段即可,规矩内部不需求判断子结构是否为空,放心大胆地直接 . 就完事儿了。(发散考虑:假如结构层级很深呢?

数据相关与打通

看懂上述内容后,答案其实现已呼之欲出了。

正如代码里所见,咱们很简单就能够把不同的数据结构进行相关。如:MyStruct->OtherStruct,只需求在一个数据结构中引证另外一个数据结构就行了。

把数据相关后,咱们也能很简单把数据打通了,让数据实现转化。只需求界说一个规矩,描绘清楚需求调查的字段,然后写完数据转化的进程。

假如数据相关引证的结构层级很深,那也没有关系。关于规矩而言,不管多深都能感知到,由于这是规矩,没错儿,这便是规矩!

关于规矩,可能刚开端不太简单理解,或感到别扭,别担心,习气就好。
一个好的诀窍是:把思维从 if … then … 转化到 when … then …

再解说清楚一点

咱们这样来想象一下:
一个程序的中心进程其实是 A>B>CZ,便是数据转化的进程。假定,中心相邻 MN 两个要害点相隔很悠远(便是说,在传统代码中,两个很难相关起来,分属八棍子撂不着的不同模块),没有关系,先在数据结构中树立相关(界说两个字段引证 MN),然后界说规矩,调查其间的 M 并将之转化为 N(便是如此简略)。重复此进程,终究就能将 A 转化为 Z

这其间,咱们再次验证了数据转化思维的有效性。每个规矩,都依赖于输入 M,然后转化为输出 N
一起,也展示出了一个风趣的隐秘现象,不知道你们发现没有?
便是,每两个要害点都能看作一个彻底独立的小规矩(和其他规矩不相关)。整个程序的数据转化链 A>B>CZ,之所以能正常作业,是由于终究这条链能够串起来!可是,风趣的是,由于每两个要害点转化的规矩都是独立的,那么就意味着,整条链也能够彻底打散,分开来写,不必纠结先后顺序,甚至都不需求关心怎么串起来。由于,整条链的规矩都写完后,这条链自然而然就串起来了,国际真奇妙。(发散考虑:那么还需求纠结先写什么后写什么吗?以及由谁来写吗?

注:唯一能让两个规矩发生相关的其实是要害点(数据字段),而不是规矩本身。所以在 规矩1 没有写完的情况下,你依然能够先写 规矩2。由于要害点(数据字段)一直就在数据结构上静静地躺着。

一个完好的开发流程

我先假定你读懂了上面的全部,假如不明白请提问出来。

上一篇文章中,咱们对事务需求进行了拆解,现在咱们要把这全部彻底、完好地串起来,看看是怎样的编程体验。

第一步,面临事务需求和流程图,把用到的数据结构写出来。彻底不需求考虑任何逻辑部分。
第二步,再次看一眼流程图,不必分析逻辑,只看流程分支节点,把条件和导向的成果找出来。它们应该都是数据结构上的字段。然后依据找出来的输入字段和输出字段,写规矩
第三步,重复第二步或第一步。
终究,由用户输入初始数据。

这便是用数据转化思维的完好编程体验。

数据转化思维是一个强壮的奥秘咒语,只需对着流程图上的逻辑进程多念几遍,就能干掉它。现在,请用力在心中铭记,毫不犹豫地大声吟诵出来,不要扭扭捏捏,大大方方地告知身边的朋友们吧。


未来的编程办法(一)
未来的编程办法(二)
面向未来的 rainforest-js