This is a follow up on my previous post on Ad-hoc Polymorphism. I highly recommend reading the previous post first. In this post we will refactor the same code to use defprotocol construct. At the end of this post you will notice that using defprotocol results in better encapsulation and fewer lines of code.
defprotocol is a means to achieving ad-hoc polymorphism. Ad-hoc polymorphism in simple terms is a technique of varying behaviour based on their types. Complex states can be modeled in Elixir using the defstruct construct. States defined using defstruct can be used as types for achieving ad-hoc polymorphism.
Lets use defprotocol to define a protocol (aka behavior) that we want to vary based on structs.
defprotocol ConnectionHandler do
def handle(con)
end
defimpl let's us implement different behaviors for protocols defined using the defprotocol construct, as shown below.
defmodule ConnectionHandler.FirstConnectionHandler do
defstruct con: nil
defimpl ConnectionHandler, for: ConnectionHandler.FirstConnectionHandler do
def handle(%ConnectionHandler.FirstConnectionHandler{con: con}) do
"First"
end
end
end
defmodule ConnectionHandler.SecondConnectionHandler do
defstruct con: nil
defimpl ConnectionHandler, for: ConnectionHandler.SecondConnectionHandler do
def handle(%ConnectionHandler.SecondConnectionHandler{con: con}) do
"Second"
end
end
end
Below shown test demonstrates how everything glues together. If you have read the previous post, you might notice that no change has been made to the test.
defmodule Blaze.Test do
use ExUnit.Case
defprotocol ConnectionHandler do
def handle(con)
end
defmodule ConnectionHandler.FirstConnectionHandler do
defstruct con: nil
defimpl ConnectionHandler, for: ConnectionHandler.FirstConnectionHandler do
def handle(%ConnectionHandler.FirstConnectionHandler{con: con}) do
"First"
end
end
end
defmodule ConnectionHandler.SecondConnectionHandler do
defstruct con: nil
defimpl ConnectionHandler, for: ConnectionHandler.SecondConnectionHandler do
def handle(%ConnectionHandler.SecondConnectionHandler{con: con}) do
"Second"
end
end
end
test "demonstration of `defprotocol`" do
alias ConnectionHandler.FirstConnectionHandler
alias ConnectionHandler.SecondConnectionHandler
assert ConnectionHandler.handle(%FirstConnectionHandler{con: []}) == "First"
assert ConnectionHandler.handle(%SecondConnectionHandler{con: []}) == "Second"
end
end
This post demonstrates that in Elixir using defprotocol for adhoc-polymorphism leads to fewer lines of code and better encapsulation.
Top comments (0)