2016-08-13 58 views
1

Elixir执行横切关注点的方式是什么?假设我想检查一个用户是否已经取消订阅电子邮件 - 是否可以在任何电子邮件发送功能执行之前这样做?在Elixir文档中找不到交错其他功能的任何内容......就像rails中的before_filter,但是在独立的Elixir应用程序中。可能会发送多个电子邮件 - 通知,市场营销,交易等。添加每个未订阅?手动检查每个功能是非常必要的。 Elixir有更好的方法吗?Elixir - 横切关注点

+0

这只是我的意见,但我认为你可以只写有一个功能,比方说,一个特殊的模块,接受用户,检查他是否有订阅,如果他这样做,发送电子邮件。然后你可以在需要它的地方使用这个模块。这样做会更加明确,不会给你带来一些回调魔术。 – JustMichael

+0

@JustMichael - 这不重复吗?我也在考虑一个模块,但由于该电子邮件模块中的每个功能都需要执行相同的操作,因此在每个函数中检查相同的内容都不符合DRY原则。此外,我正在寻找Elixir在尝试学习语言时提供的不同替代方案 – sat

+0

DRY原则不是一项法律 - 只是一个很好的指导方针。有时候不可能避免重复。虽然不重复代码是一个伟大的目标,但可读和可维护的代码也很重要。另外,你认为AOP如何实现这一点?你认为他们不重复代码来做到这一点?它只是隐藏在视野之外。 –

回答

0

在Rails中,使用before_filter实际上导致了糟糕的设计。我知道用这种方式编写代码更容易,但是过了一段时间,代码库会不断增加,在这些回调中添加更多的逻辑,并且开始放松对它们的控制。

您可以在不使用回调的情况下使用简单的函数来执行相同的操作,并且可以通过一种必要的方式保持控制(更容易推理和调试)。

假设你有一个具有多个电子邮件功能

defmodule Mailer do 
    def send_email1(params) do 
    # do work 
    end 

    def send_email2(params) do 
    end 

    # ... 
end 

取而代之的模块添加某种可以简单的代码添加到每个这些功能正确的before_filter?这很简单,很容易理解发生了什么。

defmodule Mailer do 
    def send_email1(params) do 
    if can_send?(params) do 
     # do work 
    end 
    end 

    # ... 
end 

现在你发现你有重复的逻辑。那么......您可以将检查逻辑移动到一个更新的功能,并至少从这些功能中删除if。但是你也可以创建一个新的抽象,并保持共享逻辑。

defmodule Mailer do 
    def send_email(email_name, params) do 
    if can_send?(params) do 
     do_send_email(email_name, params) 
    end 
    end 

    defp do_send_email("email1", params) do 
    # do work 
    end 
    # ... 
end 

这比任何带回调的替代方法都简单得多。你保持对它的控制。如果需要,添加或更改共享逻辑并添加条件逻辑很容易。

0

我喜欢使用Elixir的module attributes来启动一连串的tail-call优化函数调用。这可以很容易地在一个地方声明你的回调。

defmodule MyMod do 

    @before_filter [:check_email] 

    def check_email(1), do: true 
    def check_email(state), do: false 

    def save(state) do 
    save(state, @before_filter) 
    end 

    defp save(state, []) do 
    # do actual save 
    {:ok, state} 
    end 

    defp save(state, [callback|tail]) do 
    if :erlang.apply(__MODULE__, callback, [state]) do 
     save(state, tail) 
    else 
     {:error, callback, state} 
    end 
    end 
end