计算表达式是面向方面编程的替代方法吗?
这是 F# 管理横切关注点的解决方案吗?
看了下面的文章,忍不住想到了AOP(即面向切面编程)。
在文章中,作者提供了一个处理日志的计算表达式的示例,但在不混淆业务逻辑的主要意图的情况下隔离了代码的实际日志方面。
我的想法准确吗?
计算表达式是面向方面编程的替代方法吗?
这是 F# 管理横切关注点的解决方案吗?
看了下面的文章,忍不住想到了AOP(即面向切面编程)。
在文章中,作者提供了一个处理日志的计算表达式的示例,但在不混淆业务逻辑的主要意图的情况下隔离了代码的实际日志方面。
我的想法准确吗?
是的,monad 是一种很好的(并且惯用的)横切关注点的方法,除此之外。monad 是一个更通用的概念,但它们的用途之一是对系统中的效果进行建模。
在 FP 行话中,效果一词通常意味着副作用。许多横切关注点,如日志记录、审计、性能监控、缓存和计量(根据定义)都有副作用。由于它们涉及将数据“写入”到辅助有状态资源,因此可以使用 State 或 Writer monad 对其进行建模。
其他跨领域问题,如身份验证、授权和验证,通常可以使用 Reader monad(或者可能是 State monad)来解决。
F# 计算表达式在单子组合符(本质上是return和bind )上提供了语法糖,这与 Haskell 的do
符号所做的方式相同。然而,与 Haskell 不同的是,您必须自己为 monad 定义计算表达式构建器,除了语言中已经内置的少数几个 ( async
, seq
)。
使用 monad 解决横切关注点是与 AOP 不同的方法。
在面向对象编程中,对 AOP 有两种根本不同的看法:
正如我在书中解释的那样,我认为编译时编织是解决问题的一种笨拙且不灵活的方法。使用装饰器是实现相同目标的一种更优雅、更灵活的方式。
我发表该声明的动机是我偏爱关注点分离。
如果您在 OOP 中使用编译时编织,您通常会有这样的代码(C# 示例):
[Log]
public void SaveOrder(Order order)
{
// Implementation goes here...
}
这里的问题是,虽然关注点是分开的,但它们仍然是耦合的。除非重新编译,否则您不能决定不登录。SaveOrder
使用计算表达式有点像:
log { return saveOrder order }
同样,横切关注点与实现一起编译。
然而,相似之处在于单子组合器可用于将关注点组合在一起,这在编译时编织中不容易实现。在 OOP 中,您不能轻易地采用不记录的方法,并在将对象组合在一起时“神奇地”使其记录。
另一方面,使用 monad,您可以获取纯函数并将其与 monadic 上下文组合。这种后期绑定的日志在结构上等同于使用装饰器。
因此,我认为将计算表达式用于横切关注点是合适的,只要您将此类代码推迟到应用程序的入口点(其Composition Root)。