在 elixir 中共享相同功能的设计模式是什么?

例如,
我有一个应用程序,它“采用”一个结构,将结构“转换”为不同的格式并将其“推送”到一些存储中。
我有 3 个结构通过这个管道,有 3 个转换规则和 3 个存储。

该项目使用 gen_stage 包,它具有以下结构:

(book)   |producer| -> |transformer| -> |indexer|
(article)|producer| -> |transformer| -> |indexer|
(post)   |producer| -> |transformer| -> |indexer|

每个阶段都是一个单独的模块,即 Book.Producer、Book.Transformer、Book.Indexer。

同一垂直线上的阶段执行 相同的事情,但具有不同的实体。 IE。 Book.Producer 从数据库中取书,Article.Producer 从数据库中取文章等。

“从数据库中获取”部分相当通用,可以在所有管道中重复使用,即
alias Experimental.GenStage

defmodule Books.Producer do
  use GenStage

  def start_link do
    GenStage.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init([]) do
    {:producer, []}
  end

  def handle_demand(demand, processed) when demand > 0 do
    events = Repo.all Book
    {:noreply, events, processed ++ events}
  end
end

转换器在从数据库生成的实体上运行一个函数。索引器将转换器记录推送到另一个存储中。

在带有大量重复的幼稚方法中,我可以有 9 个(3 个用于书籍,3 个用于文章,3 个用于发布)模块来完成所有这些步骤。

我有什么选择来提取类似的功能并在使用它的模块之间共享它。

也许我可以通过在初始化阶段传递参数来配置它,或者仍然有 9 个模块,但将函数重构到另一个模块中。最佳做法是什么?

最佳答案

对于您提到的情况,一个可能的选择是在启动流程时(例如,从监督树)传递您尝试从中获取的架构模块(即 BookPost 等)作为选项。您将在对 start_link 的调用中收到这些选项,然后可以在调用 GenStage.start_link 时将它们作为第二个参数传递。

这将导致这些选项在 GenStage 启动时作为参数传递给 init。此时,您可以将该模块置于状态(例如,与元组中的 processed 列表一起),以便在每次 handle_demand 调用时传递它。在那里,您最终可以将其用作调用 Repo.all 的参数。

模块可以像任何其他值一样传递,有时您可以利用它来重用代码 - 这是一个可能派上用场的示例!

您可以对流程的其他部分使用类似的方法 - 只需弄清楚哪些变化并在启动流程时将其指定为选项即可。例如,转换函数将是您第二步的一个选项的一个很好的候选者。有几种方法可以实现:

  • 传入实现某个功能的模块 - 查看 Behaviours 上的文档以确保该功能实际存在于模块中;
  • 传入现有模块中捕获的函数,例如 &BookTransformer.transform/1
  • 传入一个匿名函数: fn data -> transform_all_the(data) end

  • 和模块一样,函数在 Elixir 中是一等公民。它们可以像值一样传递。这是函数式编程语言中常见的代码重用模式。

    关于elixir - 在 elixir 中共享相同功能的设计模式是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39072491/

    10-13 06:13