如何避免Elixir中的嵌套if语句?

如何避免Elixir中的嵌套if语句?,elixir,Elixir,有没有办法在Elixir中重构它,使它更具可读性 def validate(params) do Repo.start_link if validate_oauth_params(params) === true do oauth_client = Repo.get_by(OauthClient, random_id: params["client_id"], secret: params["secret"]) if oauth_client !=

有没有办法在Elixir中重构它,使它更具可读性

  def validate(params) do
    Repo.start_link

    if validate_oauth_params(params) === true do
      oauth_client = Repo.get_by(OauthClient, random_id: params["client_id"], secret: params["secret"])

      if oauth_client != nil do
        allowed_grant_types = Poison.Parser.parse!(oauth_client.allowed_grant_types)
        if Map.has_key?(allowed_grant_types, params["grant_type"]) do
          case params["grant_type"] do
            "password" ->
              process_password_grant(params, oauth_client)
            "refresh_token" ->
              process_refresh_token_grant(params["refresh_token"], oauth_client)
            "client_credentials" ->
              process_client_credentials_grant(oauth_client)
            nil ->
              %{message: "Invalid oauth credentials", code: 400}
          end
        end
      else
        %{message: "Invalid oauth credentials", code: 400}
      end
    else
      %{message: "Invalid oauth credentials", code: 400}
    end
  end

因为这段代码看起来像PHP,所以它的灵丹妙药是什么。不是我写的。

你说得对,它看起来像PHP。没有使用类似长生不老药的模式匹配的好处

重构这一部分是很困难的,因为似乎其他方法也应该重构,以使其更干净。例如,
validate_oauth_params
函数“可以”返回一个元组而不是布尔值,因此可以对其进行模式匹配,操作如下:

def validate(params) do
  case validate_oauth_params(params) do
    {:ok, params} -> choose_oauth_method params
    {:error} -> handle_error "Invalid params"
  end
end

defp choose_oauth_method(%{"grant_type" => "password"} = params) do
  process_password_grant(params)
end
defp choose_oauth_method(%{"grant_type" => nil}) do
  handle_error "Method undefined"
end

defp handle_error(msg), do: %{message: msg, code: 400}
defp handle_error(msg, code), do: %{message: msg, code: code}
defp handle_error(), do: %{message: "Default error massage", code: 400}

类似这样的代码根本不起作用,只是想说明一下模式匹配的工作原理及其优点

您可以尝试使用
with
表达式将此代码重写为

def validate(params) do
  with
    {:ok, p} <- validate_oauth_params(params),
    {:ok, client} = get_client_for_params(p),
    {:ok, allowed_grant_types} <- Poison.Parser.parse(oauth_client.allowed_grant_types)
  do
    case params["grant_type"] do
      "password" ->
        process_password_grant(params, oauth_client)
      "refresh_token" ->
        process_refresh_token_grant(params["refresh_token"], oauth_client)
      "client_credentials" ->
        process_client_credentials_grant(oauth_client)
      nil -> :error
    end
  end |> case do
    {:ok, something} -> something
    _ -> %{message: "Invalid oauth credentials", code: 400}
  end
end
def验证(参数)do
具有
{:好的,p}
处理刷新令牌授权(参数[“刷新令牌”],oauth\u客户端)
“客户端凭据”->
处理客户端凭据授权(oauth客户端)
无->:错误
结束
结束|>case do
{:好的,什么东西}->什么东西
_->%{消息:“无效的oauth凭据”,代码:400}
结束
结束

然而,这样的重构可能需要在其他代码中进行更多的更改,或者引入辅助函数来返回可识别的值和错误<带有的代码>目前不支持模式中的防护,但在1.3中会支持

当逻辑变得非常复杂,并且with/do语句不够时,我喜欢使用一种不同的方法

def validate(params) do
    try do
        if !validate_oauth_params(params), do: 
            throw({:error, %{message: "Invalid oauth credentials", code: 400}})

        if !oauth_client, do: 
            throw({:error, %{message: "Invalid oauth credentials", code: 400}})

        allowed_grant_types = Poison.Parser.parse!(oauth_client.allowed_grant_types)
        if !Map.has_key?(allowed_grant_types, params["grant_type"]), do:
            throw({:error, %{message: "No grant type", code: 400}})

        case params["grant_type"] do
            "password" ->
                process_password_grant(params, oauth_client)
            "refresh_token" ->
                process_refresh_token_grant(params["refresh_token"], oauth_client)
            "client_credentials" ->
                process_client_credentials_grant(oauth_client)
            nil ->
                throw({:error, %{message: "Invalid oauth credentials", code: 400}})
        end
    catch
        {:error, map} -> {:error, map}
    else
        result -> {:ok, result}
    end
end

将代码分解为子函数。我现在没有时间重新编写代码,但将最里面的
if
抽象为一个单独的函数,并可能对另一个
if
也这样做。您的开发人员应该学习这本很棒的入门指南,至少要理解关键点:似乎这个问题在代码审查交换中更为常见。如何将它转移到那里?希望有办法。很抱歉。我以为这里有更多的潜伏者。看起来更有条理,但仍然很混乱。你最后的
handle\u error
子句永远不会匹配。它与第一个等效,但忽略了参数。您是否希望它只是
handle\u error()
——没有参数?