Elixir의 프로토콜 대 행동: 추가 고려 사항
초기 생각
최근에 나는 Elixir의 Protocols vs. Behaviors에 대한 환상적인article by Yiming Chen을 읽었습니다.
나는 훌륭한 기사에 대해 저자에게 감사하고 싶습니다. 이 기사는 많은 것을 명확하게 하고 제가 혼자서는 얻을 수 없는 몇 가지 아이디어를 엄격하게 공식화합니다.
여기에 Protocols vs. Behaviours의 사용 사례에 대한 몇 가지 생각을 추가하고 싶습니다. 정확히 왜 우리는 여전히 Protocols 대신 Behaviors를 자주 사용합니다.
제 개인적인 비공식 요지는 다음과 같습니다.
Protocols are suitable for pure interfaces (as stated in the article) of pure data structures.
프로세스와 메시지 전송과 같은 부작용을 다루는 프로토콜을 사용하려고 할 때 종종 어색함을 느끼기 시작합니다.
그 이유는 부작용이 있는 것은 OTP 조각( GenServer
)으로 빌드되고 다음과 같습니다.
Protocols are suitable for pure interfaces (as stated in the article) of pure data structures.
삽화
그것을 설명하겠습니다.
나는 작은 라이브러리SMPPEX를 개발하고 거기에 Session
라는 모듈과 동작이 있습니다. 감싸고 확장하는 것만 중요합니다GenServer
, 즉
init
, handle_call
, ... 및 일부 자체 콜백(handle_pdu
등)과 같은 콜백을 지정합니다. 일반적으로 작동합니다. init
일부 상태를 초기화한 다음 콜백. Session.start_link
, Session.call
, Session.cast
, Session.send_pdu
등과 같은 인터페이스 기능을 구현합니다. 순진한 디자인
이 모듈을 처음 구현하려고 했을 때 프로토콜이라는 아이디어를 적용하려고 했습니다.
콜백을 지정하는
SessionState
와 같은 프로토콜을 만들었습니다.defprotocol SessionState do
def handle_call(st, from, message)
def handle_pdu(st, pdu)
...
end
Session
를 사용하려면 다음을 수행합니다.먼저
SessionState
를 구현합니다.defmodule SessionStateImpl do
defstruct [...]
def new(args) do
...
end
end
defimpl SessionState, for: SessionStateImpl do
def handle_pdu(st, pdu) do
...
{:noreply, new_st}
end
end
그런 다음 인스턴스를 만들고
Session
에 전달합니다.st = SessionStateImpl.new(args)
{:ok, pid} = Session.start_link(st, ...)
얼핏 보기에는 모든 것이 훌륭해 보이지만 PoC 앱을 만들다가 문제에 봉착했습니다. OTP에서 초기화 컨텍스트가 필수적이라는 것입니다.
GenServers
를 구현할 때 사용자는 종종 다음과 같은 작업을 하고 싶어합니다.def init(opts) do
...
timer = :erlang.start_timer(@interval, self(), :tick)
...
end
타이머를 설정하거나 전역 등록 등과 같은 다른 유용한 작업을 수행하려면 세션의 PID를 알아야 합니다.
하지만
SessionStateImpl
시작하기 전에 SessionStateImpl.new
(call Session
)를 생성하고 그렇게 할 장소가 없습니다.가능한 개선 사항
솔루션 1
SessionStateImpl
내부가 그렇게 할 수 있도록 Session
생성 및 초기화를 연기할 수 있습니다.# st = SessionStateImpl.new(args)
{:ok, pid} = Session.start_link(SessionStateImpl, :new, args, ...)
그러나 우리가 Protocol을 사용하고 모듈과 상태를 전달하지 않고 다시 전달하기 시작했다고 생각하지 않습니다. :)
솔루션 2
초기화를 연기하고
SessionState
프로토콜의 일부로 만들 수 있습니다. 문제는 기본 구조체 없이 Protocol을 사용할 수 없다는 것이므로 다음과 같이 해야 합니다.defprotocol SessionState do
...
def init(uninitialized_st, args)
...
end
...
defimpl SessionState, for: SessionStateImpl do
def init(uninitialized_st, args) do
...
# some initialization
...
{:ok, initialized_st}
end
end
...
st = %SessionStateImpl{}
{:ok, pid} = Session.start_link(st, args, ...)
이제 우리는 초기화되지 않은 상태를 전달해야 합니다. 제가 별로 좋아하지 않는 아이디어입니다.
동작 사용
이것으로 놀면서 나는 이상한 일을하고 있고 가능한 사용자를 혼란스럽게 할 것이라고 결론 지었습니다.
나는 전통적인 행동으로 옮겼고 모든 것이 옳았다는 느낌을 받았습니다. :)
프로토콜 사용의 성공적인 예
일단 git 커밋을 분석하는 시스템을 개발 중이었습니다. 커밋은 Bitbucket API에서 가져왔고 전달되는 방대한 JSON 데이터 청크를 나타냅니다.
Commit
프로토콜을 추가하고 APICommit
구현했습니다.defprotocol Commit do
def author(commit)
def author_email(commit)
def added_files(commit)
...
end
나중에 그런 커밋을 DB에 다르게 저장된 커밋과 결합해야 했습니다. 그러나 프로토콜이 있는 덕분에 이 논리는
Commit
데이터 구조에 대해 DBCommit
를 구현한 후 다르게 취급할 필요가 없었습니다.이 프로토콜 사용 사례는 순수 데이터 구조를 다루는 예이며 성공적인 것으로 판명되었습니다.
결론
Elixir에서 동작과 프로토콜 중에서 선택하는 문제는 흥미롭고 동시에 도전적입니다.
행동 및 프로토콜을 사용한 모델링 시도의 성공 및 실패 사례를 더 많이 볼 수 있기를 바랍니다.
Reference
이 문제에 관하여(Elixir의 프로토콜 대 행동: 추가 고려 사항), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/savonarola/protocols-vs-behaviours-in-elixir-additional-thoughts-56l
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(Elixir의 프로토콜 대 행동: 추가 고려 사항), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/savonarola/protocols-vs-behaviours-in-elixir-additional-thoughts-56l텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)