【问题标题】:How to specify module implementation at compile time in Elixir?如何在 Elixir 的编译时指定模块实现?
【发布时间】:2021-04-07 02:33:24
【问题描述】:

我有一个应用程序应该支持多种类型的存储。目前,我希望它同时支持 S3 和 swift。

现在的问题是:我如何允许我的应用程序选择在加载时使用哪个后端(例如,配置将指定它是 S3 还是 swift)?

在 OOP 中,我将拥有接口,并且可以注入依赖项。在这种情况下,显而易见的答案是 GenServer。但我实际上并不需要一个完整的专用进程(它也应该成为我的代码的阻塞点)。

我考虑过简单地将模块的引用作为参数传递,但感觉有点不确定,因为从技术上讲,可能会传递不正确的实现。

因此进一步说明:如何根据配置(没有 Genserver)将特定后端(S3 或 swift)注入我的代码?

defmodule App.Filestorage do
    @callback store(String.t) :: :ok | :error
end

defmodule App.S3 do
    @behaviour App.Filestorage
    @impl
    def store(path), do: :ok 
end

defmodule App.Swift do
    @behaviour App.Filestorage
    @impl
    def store(path), do: :ok
end

defmodule App.Foo do
    def do_stuff()
        # doing some stuff
        App.Filestorage.store(result_file) # replace this with S3 or Swift
    end
end

【问题讨论】:

    标签: elixir


    【解决方案1】:

    你是在正确的方式! 要通过配置使用注入,您可以指定所选可执行模块的名称并从配置中获取其名称以调用它:

    config:
    config :app, :filestorage, App.S3
    
    
    defmodule App.Filestorage.Behaviour do
        @callback store(String.t) :: :ok | :error
    end
    
    defmodule App.Filestorage do
      def adapter(), do: Application.get_env(:app, :filestorage)
      def store(string), to: adapter().store
    end
    
    defmodule App.S3 do
        @behaviour App.Filestorage.Behaviour
        @impl true
        def store(path), do: :ok 
    end
    
    defmodule App.Swift do
        @behaviour App.Filestorage.Behaviour
        @impl true
        def store(path), do: :ok
    end
    
    defmodule App.Foo do
        def do_stuff()
            # doing some stuff
            App.Filestorage.store(result_file) # replace this with S3 or Swift
        end
    end
    

    注意 1:如果需要,您可以合并行为(App.Filestorage.Behaviour) 和“实现”(App.Filestorage) 模块

    注意 2:您可以使用模块属性从配置中指定适配器,但请注意部署期间的副作用,因为它将保存编译期间的确切配置

    注意 3:如果您按功能使用适配器规范,就像在示例中一样,您甚至可以在运行时通过更改配置来更改选定的实现

    更多详情见帖子: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ https://blog.carbonfive.com/lightweight-dependency-injection-in-elixir-without-the-tears/

    【讨论】:

    • 谢谢,这是一个有趣的解决方案。虽然是一个问题,只是为了正确理解 - 这会牺牲类型安全,不是吗?如中,可以在配置中注入不正确的模块。
    • 将配置视为代码库的一部分 - 如果您以某种方式设置了具有相同功能的错误模块 - 这将与您直接在代码中使用错误模块相同。如果您的意思是通过用户输入进行注入 - 它不会发生
    猜你喜欢
    • 2017-10-31
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    • 2019-11-24
    • 2023-03-15
    • 1970-01-01
    相关资源
    最近更新 更多