随着时间的推移,我注意到一种设计启发式方法,它极大地帮助了我完成无数项目。
这种启发式的地方在于它在概念上易于理解和应用,但它自然会引导您更接近函数式编程。
事实上,这与 Haskell 处理 IO 的方式非常相似。它也是 React 等现代 Web/UI 框架的核心理念。
为了说明这一点,让我们看一个例子:
你正在处理一个涉及安排未来用户通知的事情:有一些输入需要考虑,比如用户喜欢的时间和时间的推移;你已经决定提前一周安排它们;你需要定期安排新的通知,但你也可能需要删除或重新安排现有的通知。
对于未来一周的每一天foreach |
在这个ifelse例子中,我们在进行处理的同时也在修改世界。
副作用本质上并不坏:事实上,它们是编写软件的全部意义所在。如果你的代码没有效果,就不会有人使用它。
不过,还是要问问自己,你怎么能把这些副作用保存到最后?
归根结底,办法就是建立一些数据,描述需要采取的任何行动。
对于未来一周的每一天foreach |
这段伪代码输出:
[{action: "remove", notificationId: 4}, |
然后你会把这些数据交给另一个更简单的函数,由这个更简单的函数来执行这些副作用。
诚然,这种启发式方法将导致你做一些额外的工作,但也有很多好处。其中。
首先,它创造了一个自然边界,对测试非常有用。上面的伪代码中的逻辑(如 "否则,是否应该重新安排?")实际上可能相当复杂。
在系统中,决定实施什么变化的逻辑往往比实施上述变化的逻辑要复杂得多。通过划定这个边界,你可以获得编写更少的艰巨的测试的好处。要模拟的东西更少,而且很容易为数据写断言。
第二,调试变得更加容易。能够问一个系统要做什么,并在它开始执行之前收到其计划的完整快照,这真的很神奇。这对于更复杂的功能来说尤其如此,例如与外部系统的同步。
最后,这种方法使你能够轻松地进行分层转换。使用上面的例子,假设你的通知调度器应该避免某些假期。你可以写一个简单的函数,接收一个调度行为的列表,并过滤掉任何发生在特定日期的行为。