在elixir中共享相同功能的设计模式是什么?
例如, 我有一个应用程序“采取”一个结构,“转换”结构到不同的格式,并“推”到一些存储。 我有3个结构通过这个管道,有3个转换规则和3个存储。
项目使用gen_stage
包,它具有以下结构:
(book) |producer| -> |transformer| -> |indexer|
(article)|producer| -> |transformer| -> |indexer|
(post) |producer| -> |transformer| -> |indexer|
每个阶段都是一个单独的模块,即Book.Producer,Book.Transformer,Book.Indexer。
在同一垂直线上执行相同的事情但具有不同实体的阶段。即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个)模块正在执行所有这些步骤。
我有哪些选项可以提取相似功能的位并在使用它的模块之间共享。
也许我可以通过在初始化阶段传递params来配置它,或者只是仍然有9个模块,但重构函数到另一个模块。什么是最佳做法?
答案 0 :(得分:1)
您提到的案例的一个可能选项是在启动流程时将您尝试从中获取的架构模块(即Book
,Post
等)作为选项传递(来自例如监督树。您将在调用start_link
时收到这些选项,然后在调用GenStage.start_link
时将其作为第二个参数传递。
这会导致在GenStage启动时将这些选项作为参数传递给init
。此时,您可以将该模块置于状态(例如,元组中的processed
列表中),以便在每次handle_demand
调用时传入该模块。在那里,您最终可以将其用作对Repo.all
的调用的参数。
模块可以像任何其他值一样传递,有时你可以利用它来重用代码 - 这是一个可以派上用场的例子!
您可以对流程的其他部分使用类似的方法 - 只需弄清楚变化的内容,并在启动流程时将其指定为选项。例如,转换函数将是第二步选项的良好候选者。有几种方法可以实现这一目标:
&BookTransformer.transform/1
; fn data -> transform_all_the(data) end
。与模块一样,函数是Elixir中的一等公民。它们可以像值一样传递。这是函数式编程语言中常见的代码重用模式。