【问题标题】:Elixir - Cross cutting concernsElixir - 横切关注点
【发布时间】:2017-01-09 07:49:41
【问题描述】:

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

【问题讨论】:

  • 这只是我的意见,但我认为您可以编写一个特殊的模块,该模块具有一个功能,比如说接受用户,检查他是否有订阅,如果有,发送电子邮件。然后你可以在任何需要的地方使用这个模块。这样会更明确,不会给你留下一些回调魔法。
  • @JustMichael - 这不是重复的吗?我也在考虑一个模块,但是由于该电子邮件模块中的每个函数都需要做同样的事情,因此在每个函数中检查相同的事情并不符合 DRY 原则。此外,我正在寻找 Elixir 在尝试学习语言时提供的不同替代方案
  • DRY 原则不是法律——只是一个很好的指导方针。有时只是无法避免重复。虽然不重复代码是一个很好的目标,但可读和可维护的代码也很重要。此外,您认为 AOP 是如何做到这一点的?你认为他们不会重复代码来做到这一点吗?它只是隐藏在视图之外。
  • @OnorioCatenacci - AOP 不重复代码。它引入了一个透明代理,它让您编写的代码执行某些操作。示例检查用户是否已通过身份验证。那不是代码重复。是的,就像任何事情一样,回调需要一种平衡的方法,以免代码作者后来忘记它而变得过于混乱
  • 是的@sat 它确实重复了代码。查找方面织布工。它根本不会向开发人员显示冗余代码。

标签: elixir


【解决方案1】:

在 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

这比任何带有回调的替代代码都简单得多。你控制它。并且很容易添加或更改共享逻辑,并在需要时为其添加条件逻辑。

【讨论】:

    【解决方案2】:

    我喜欢使用 Elixir 的 module attributes 来启动一系列尾调用优化的函数调用。这使得在一个地方声明所有回调变得容易。

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-05
      • 2011-08-01
      • 2020-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多