Я хочу «расширить» встроенные функции ввода/вывода Elixir несколькими (независимыми) способами.Как реализовать полиморфизм функций/процессов в Elixir
я придумал следующий шаблон кода:
defmodule FileExtension1 do
# arguments are different for different extensions,
# but I guess I could just stick with single map/list
# argument for uniformity
def open!(filename, some_param \\ true) do
filename
|> File.open!
|> attach(some_param)
end
def close(io) do
io |> File.close
end
def attach(io, some_param \\ false) do
spawn_link fn ->
file_manager(io, some_param)
end
end
def detach(io) do
io |> send {:detach, self}
receive do
{:will_detach, ^io} -> :ok
end
end
defp file_manager(io, some_param, state \\ <<>>) do
if Process.alive?(io) do
receive do
{:detach, sender} ->
sender |> send {:will_detach, self}
{:custom_request, sender, reference, count} ->
# {result, new_state} = do_job(...)
sender |> send {:custom_reply, reference, result}
file_manager(io, some_param, new_state)
{:io_request, sender, reference, {:some_pattern}} ->
# {result, new_state} = do_job(...)
sender |> send {:io_reply, reference, result}
file_manager(io, some_param, new_state)
x ->
io |> proxy(x)
file_manager(io, some_param, state)
end
end
end
defp proxy(io, data) do
{request_type, original_from, original_reference, command} = data
reference = make_ref
io |> send {request_type, self, reference, command}
receive do
{response_type, ^reference, result} -> original_from |> send {response_type, original_reference, result}
end
end
end
В основном, это делает следующее:
- Ручку пользовательских IO запросов в соответствии с пользовательским протоколом
- Модифицирует обработки некоторыхов стандартные запросы ввода-вывода
- Прокси-серверы все остальное до базового
File
Теперь я могу прозрачно складывать эти вещи друг на друга (т. attach
первый на File
, затем второй на первый и т. Д.).
Проблема: теперь у меня есть три модуля, которые следуют тому же образцу, который я описал выше. Я хочу как-то удалить дублирование кода.
На что я должен смотреть?
- Просто создайте еще один модуль с общими функциями? Например,
IO
содержит общие функции для нескольких устройств, илиEnum
содержит общие функции для нескольких типов. Не могли бы вы дать мне небольшой пример того, как это будет работать в моем случае? - Протоколы? Я не совсем понимаю, как протоколы можно использовать здесь, потому что то, что я пытаюсь достичь, не распространяется на «использование некоторой функции для разных (встроенных) типов».
- Поведение? Похоже, я мог бы выиграть от создания какого-то варианта
GenServer
, настроенного на мои нужды. Опять же, если я буду использовать это, небольшой пример поможет здесь.
Вопрос с бонусом: Как проверить, что общая функциональность используется с помощью ExUnit
?
Почему вы создаете различные функции? Если вам нужно другое поведение для разных расширений файлов, просто передайте поведение. Серьезно - просто передайте функцию для обработки расширения файла. Вы делаете это намного сложнее, чем должно быть. –
@OnorioCatenacci Мне нравится эта идея. Не могли бы вы добавить действительно минималистический пример в качестве ответа? – EugZol
Если у меня будет время, да, я добавлю пример. –