在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个用于Book,3个用于Article,3个用于Post)模块正在执行所有这些步骤.
我有哪些选项来提取类似功能的位并在使用它的模块之间共享它.
也许我可以通过在初始化阶段传递params来配置它,或者只是仍然有9个模块,但重构函数到另一个模块.什么是最佳做法?
最佳答案 您提到的案例的一个可能选项是在启动流程时(例如,从监督树)传递您尝试从中获取的架构模块(即Book,Post等)作为选项.您将在调用start_link时收到这些选项,然后可以在调用GenStage.start_link时将它们作为第二个参数传递.
这将导致在GenStage启动时将这些选项作为参数传递给init.此时,您可以将该模块置于状态(例如,在元组中处理列表中),以便在每次handle_demand调用时传入该模块.在那里,你最终可以将它用作Repo.all调用的参数.
模块可以像任何其他值一样传递,有时你可以利用它来重用代码 – 这是一个可以派上用场的例子!
您可以对流程的其他部分使用类似的方法 – 只需确定变化的内容,并在启动流程时将其指定为选项.例如,转换函数将是第二步选项的良好候选者.有几种方法可以实现这一目标:
>传入一个实现某个功能的模块 – 查看Behaviours上的文档,以确保该功能实际存在于模块中;
>传入现有模块中的捕获函数,例如& BookTransformer.transform / 1;
>传递一个匿名函数:fn data – > transform_all_the(数据)结束.
像模块一样,功能是Elixir的一等公民.它们可以像值一样传递.这是函数式编程语言中常见的代码重用模式.