【问题标题】:Refactoring an OOP "decorator" to Free monad structure(s)将 OOP“装饰器”重构为 Free monad 结构
【发布时间】:2017-03-13 07:21:36
【问题描述】:

我有一些“遗留”的 Scala 代码(类似 Java),它执行一些数据访问。有一个装饰器可以跟踪 DAO 方法(收集指标)的使用情况,如下所示:

class TrackingDao(tracker: Tracker) extends Dao {
    def fetchById(id: UUID, source: String): Option[String] = {
        tracker.track("fetchById", source) {
            actualFetchLogic(...)
        }
    }
    ...
}

我正在尝试将其建模为 Free monad。我为 DAO 操作定义了以下代数:

sealed trait DBOp[A]
case class FetchById(id: UUID) extends DBOp[Option[String]]
...

我看到两个选项:

a) 我可以制作两个接受DBOp 的解释器,一个执行实际数据访问,另一个执行跟踪,然后将它们组合在一起或
b) 我将 Tracking 设为显式代数,并使用 Coproduct 在相同的 for 组合中使用它们或
c) 完全不同的东西!

第一个选项看起来更像是一种“装饰器”方法,它与DBOp 相关联,第二个是更通用的解决方案,但需要显式调用“跟踪”代数。

另外,请注意原始fetchById 调用中的source 参数:它仅用于跟踪。我宁愿从 API 中删除它。

这是一个实际的问题:如何为跟踪建模?

【问题讨论】:

    标签: scala functional-programming scala-cats free-monad


    【解决方案1】:

    您的问题并不完全清楚,但如果跟踪是一种环境效应,当您执行数据库访问时应该“发生”并且source 只是用于跟踪目的的参数,您可能不必在你的免费语言。例如,您可以使用您现在拥有的 ADT 并解释为 (Tracker, Source, OtherStuff) => IO[A],因此您得到的是一个函数,一旦您给它一个 Tracker 和源代码以及您需要的任何其他内容,它就会生成一个程序来进行 DB 访问(例如数据库连接),并且跟踪实现对解释器是完全私有的。这让您编写数据库程序时根本无需考虑跟踪。

    另一方面,如果您确实需要谈论您的业务逻辑中的跟踪,那么我们可能需要更多关于拥有多个 Trackers 和 sources 和它们是如何被引入和使用的。处理您需要表达的内容可能需要副产品或扩展语言或嵌套语言。

    【讨论】:

    • 非常感谢您的回答,我最终按照您的建议做了一些事情!传递“来源”最终成为口译员的私人关注点。再次感谢!
    【解决方案2】:

    与我们行业的所有事物一样,直接的答案是“视情况而定”:)。由于“跟踪”在这里是一个模糊的概念(我不知道该领域的细节),我会说你有两种可能的情况(或者至少我看到了两种)

    a) “跟踪”是您业务词汇的一个元素

    如果跟踪是您企业使用的词汇中的一个单独关注点,那么我将使用一个单独的代数来表示该关注点。与此类似的东西是“身份验证和授权” - 即使它是“低级”问题,它仍然是业务语言的一部分(“作为管理员,我想......”)我会用单独的代数去这里

    b) “跟踪”是一些“调试”、“记录”的机制

    如果跟踪不是语言的一部分,而是您为维护而保留的机器元素,那么我会将其保留在它所属的地方 - 机器。我会选择一个解释器,它会对“跟踪”(记录、调试)这些不同的调用产生副作用。

    换句话说,如果现在你没有一个测试来测试“如果我做这件事,那么应该跟踪这个”,那么我肯定会选择选项 b)

    【讨论】:

    • 我最终接受了 Rob,但感谢您的回答 :)
    猜你喜欢
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2019-11-27
    • 2019-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    相关资源
    最近更新 更多