iT邦幫忙

2021 iThome 鐵人賽

DAY 8
0
Modern Web

連線網頁卡牌遊戲(Elixir, Phoenix, Liveview)系列 第 8

8 稍微重構一下下,一點就好

昨天我們按照要改動的事項一個一個的做了出牌的方法
今天來調整一下

  def play_card(pid, :host, card) do
    GenServer.cast(pid, {:play_card, :host, card})
  end

  def handle_cast({:play_card, :host, card}, game) do
    %{host: host} = game

    # 從 hand 裡面拿掉一張 1 
    new_hand = host.hand -- [card]

    # 把 1 加在 host 的 desk 裡面
    new_desk = host.desk ++ [card]

    # 組成新的 host 來替換舊的
    new_host = Map.merge(host, %{hand: new_hand, desk: new_desk})

    # 組成新的 game 來替換舊的
    new_game = Map.replace(game, :host, new_host)

    # 讓 process 使用新的 game
    {:noreply, new_game}
  end

首先我們先解決 guest 還不能出牌的問題,
其實就複製一份 handle_cast({:play_card, :host, card}, game) 方法
再把裡面的 host 都改成 guest 就可以了,但是這樣變超長,
再更改之前先講一下同名方法。

elixir 同名方法

elixir 可以有同名方法,呼叫的時候會從上面開始 pattern matching 變數,
舉例來說

defmodule Lunch do
  def eat("apple") do
  end

  def eat("cake") do
  end

  def eat(other) do
  end
end

如果我呼叫 Lunch.eat("apple")
就會對應到第一個

如果我呼叫 Lunch.eat("pie")
因為前兩項都不符合,所以會到第三個,並把 other 變數賦予 "pie"


我們試著 dry 一點,一點就好

  def handle_cast({:play_card, :host, card}, game) do
    %{host: host} = game
    new_hand = host.hand -- [card]
    new_desk = host.desk ++ [card]
    new_host = Map.merge(host, %{hand: new_hand, desk: new_desk})
    new_game = Map.replace(game, :host, new_host)
    {:noreply, new_game}
  end
  def handle_cast({:play_card, :guest, card}, game) do
    %{guest: guest} = game
    new_hand = guest.hand -- [card]
    new_desk = guest.desk ++ [card]
    new_host = Map.merge(guest, %{hand: new_hand, desk: new_desk})
    new_game = Map.replace(game, :guest, new_host)
    {:noreply, new_game}
  end

我們來做一個方法把重複的地方拉出來

  def handle_cast({:play_card, :host, card}, game) do
    %{host: host} = game
    new_host = play_card_helper(host)
    new_game = Map.replace(game, :host, new_host)
    {:noreply, new_game}
  end
  def handle_cast({:play_card, :guest, card}, game) do
    %{guest: guest} = game
    new_guest = play_card_helper(guest)
    new_game = Map.replace(game, :guest, new_host)
    {:noreply, new_game}
  end
  defp play_card_helper(data) do
    new_hand = data.hand -- [card]
    new_desk = data.desk ++ [card]
    Map.merge(data, %{hand: new_hand, desk: new_desk})
  end

再把那些等號拆掉

  def handle_cast({:play_card, :host, card}, game) do
    %{host: host} = game
    {:noreply, Map.replace(game, :host, play_card_helper(host, card))}
  end
  def handle_cast({:play_card, :guest, card}, game) do
    %{guest: guest} = game
    {:noreply, Map.replace(game, :guest, play_card_helper(guest, card))}
  end
  defp play_card_helper(data, card) do
    Map.merge(data, %{hand: data.hand -- [card], desk: data.desk ++ [card]})
  end

我們可以再 pattern matching 方法的過程,直接從 game 裡面取用 %{host: host}

  def handle_cast({:play_card, :host, card}, %{host: host} = game) do
    {:noreply, Map.replace(game, :host, play_card_helper(host, card))}
  end

  def handle_cast({:play_card, :guest, card}, %{host: host} = game) do
    {:noreply, Map.replace(game, :guest, play_card_helper(guest, card))}
  end

  defp play_card_helper(data, card) do
    Map.merge(data, %{hand: data.hand -- [card], desk: data.desk ++ [card]})
  end

好像可以改的更好,至少更好讀一點,比起這樣塞成一行
但是先這樣吧,還有很多功能要做

如果是以別人好讀懂的方向來調整,你會怎麼做呢?

(別人就是未來的自己哈哈)


上一篇
7 重做 Game struct 與 出牌方法
下一篇
9 結束這回合
系列文
連線網頁卡牌遊戲(Elixir, Phoenix, Liveview)32

尚未有邦友留言

立即登入留言