協定是 Elixir 中用來實現多型的機制,讓行為可以根據資料型態而有所不同。
協定使用defprotocol定義,其中包含 1 個或多個函式標頭。
defprotocol Reversible do
def reverse(term)
end
協定可以用defimpl來實作。
defimpl Reversible, for: List do
def reverse(term) do
Enum.reverse(term)
end
end
協定可以為任何現有的 Elixir 資料型態或結構體實作。
呼叫協定的函式時,會根據第一個引數的型別自動選擇合適的實作。
你在開發一款屬於自己的角色扮演遊戲。在你的遊戲裡,有_角色_和_物品_。你可以對物品做的眾多動作之一,就是讓角色把它吃掉。
不是所有物品都能吃,也不是所有能吃的物品都會對角色產生同樣的效果。有些物品被吃掉後會變成另一種物品(例如你吃掉一顆蘋果,就只剩下蘋果核)。
為了保有這樣的彈性,你決定建立一個Edible協定,讓部分物品可以實作它。
建立RPG.Edible協定。這個協定只有一個函式:eat。eat函式接受一個物品和一個角色,並回傳一個副產品和一個角色。
為RPG.LoafOfBread物品實作RPG.Edible協定。一條麵包被吃掉時,會給角色 5 點生命值,而且沒有副產品。
RPG.Edible.eat(%RPG.LoafOfBread{}, %RPG.Character{health: 31})
# => {nil, %RPG.Character{health: 36, mana: 0}}
為RPG.ManaPotion物品實作RPG.Edible協定。魔力藥水被吃掉時,會給角色等同於藥水強度的魔力點數,並產生一個空瓶。
RPG.Edible.eat(%RPG.ManaPotion{strength: 13}, %RPG.Character{mana: 50})
# => {%RPG.EmptyBottle{}, %RPG.Character{health: 100, mana: 63}}
為RPG.Poison物品實作RPG.Edible協定。毒藥被吃掉時,會奪走角色所有的生命值,並產生一個空瓶。
RPG.Edible.eat(%RPG.Poison{}, %RPG.Character{health: 3000})
# => {%RPG.EmptyBottle{}, %RPG.Character{health: 0, mana: 0}}