在函数调用中隐藏状态

Hide state in function call

我考虑在函数调用中隐藏状态。 示例:

body_to_client(Req, ClientRef) ->
  case cowboy_req:body(Req) of
    {ok, Data, Req2} ->
      ok = hackney:send_body(ClientRef, Data),
      Req2;
    {more, Data, Req2} ->
      ok = hackney:send_body(ClientRef, Data),
      body_to_client(Req2, ClientRef)
  end.

但我想将这段代码分成几个部分并隐藏实现:

body_to_client(ReadRequestBody, ClientRef) ->
  case ReadRequestBody() of
    {ok, Data} ->
      ok = hackney:send_body(ClientRef, Data),
      ReadRequestBody;
    {more, Data} ->
      ok = hackney:send_body(ClientRef, Data),
      body_to_client(ReadRequestBody, ClientRef)
  end.

其中 ReadRequestBody 是一个包含所有详细信息和状态的函数。 我认为应该是这样的:

ReadRequestBody = fun() ->
    {Status, Data, Req2} = cowboy_req:body(Req),
    {Status, Data}
  end

但我不知道如何处理 Req2 并在下次调用时传递它。

提取 Req2 的唯一方法是 return 它与 ReadRequestBody 的其余结果:

ReadRequestBody = fun() ->
    {Status, Data, Req2} = cowboy_req:body(Req),
    {{Status, Data}, Req2}
  end

然后你必须检查,就像你以前做的一样,但现在有更多的语法,一个你不需要的闭包和更多的行来分割结果和匹配它的内容,等等。头发着火了!啊!让我们把音量调低一点...

我觉得这整件事变得过于复杂了,所以也许转向另一个方向,删除智能设备而不是添加它们,揭示了一些东西。让我们看看没有 case:

的样子
body_to_client({ok, Data, Req}, ClientRef) ->
    ok = hackney:send_body(ClientRef, Data),
    Req;
body_to_client({more, Data, Req}, ClientRef) ->
    ok = hackney:send_body(ClientRef, Data),
    body_to_client(cowboy_req:body(Req), ClientRef).

现在 body_to_client/2 是一个独立的命名案例。但是我们两次都在做同样的事情(总是将数据发送到 ClientRef),写两次感觉很傻。关于是否迭代,实际上只有一个决定点。让我们提炼出来:

body_to_client({Status, Data, Req}, ClientRef) ->
    ok = hackney:send_body(ClientRef, Data),
    check_status({Status, Req} ClientRef).

check_status({ok, Req}, _) ->
    Req;
check_status({more, Req}, ClientRef) ->
    body_to_client(cowboy_req:body(Req), ClientRef).

现在一切都已命名,代码明确说明了每一部分的作用。如果 check_status/2 看起来更易读,我们可以将 check_status/2 作为 case 带回 body_to_client/2 中:

body_to_client({Status, Data, Req}, ClientRef) ->
    ok = hackney:send_body(ClientRef, Data),
    case Status of
        ok   -> Req;
        more -> body_to_client(cowboy_req:body(Req), ClientRef)
    end.

请注意,将作为命名函数正常工作的匹配项作为 case 引入另一个函数有些争议。 body_to_client 这个名字现在是一个小谎言,因为这个函数不仅仅是将正文发送给客户端(但这个谎言的性质可能重要也可能无关紧要,具体取决于代码的其余部分)。