In many production systems you'll want to have one module capable of talking to many potential implementations of a collaborator module (e.g a in memory cache, a redis-based cache etc). While testing it's useful to control which module the module under test is talking to. Here are the approaches I can see. The two points that seem to divide the approaches are their tool-ability (dialyzer) and their ability to handle stateful implementations (which need a `pid`). ## Passing modules Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). ```elixir defmodule Cache do use Behaviour defcallback cached?(any,any) :: boolean defcallback put(any,any) :: nil end defmodule Cache.Memory do def put(set,x) do: Set.add set, x def cached?(set,x) do: Set.member? map, x end defmodule Cache.Redis do def put(redis_pid,x) do {:ok,1} = Redis.set redis_pid, x, 1 end def cached?(redis_pid,x) do {:ok,x} = Redis.get(redis_pid,x) x != nil end end # usage defmodule UsesCache do def start(cache,cache_pid) do cache.put(cache_pid,:hello) true = cache.cached?(cache_pid,:hello) end end UsesCache.start(Cache.Memory,HashSet.new) ``` Similar idea to duck-typing. + simple - dializer(?) - modules with state - you'd have to pass a `pid` too, e.g `{module,pid}` (eugh) ## Protocols + values Write a `Protocol` for the functionality. You can then pass in an opaque value to collaborators, and the implementation will be decided at runtime. + handles stateful and stateless implementations easily + dialyze-able - requires stub implementations for testing ```elixir defprotocol Cache do def cached?(id,item) def put(id,item) end defmodule Cache.Memory do defstruct set: nil alias __MODULE__, as: Mod defimpl Cache, for: Mod do def put(%Mod{set: set},x) do: Set.add set, x def cached?(%Mod{set: set},x) do: Set.member? map, x end end defmodule Cache.Redis do defstruct redis: nil alias __MODULE__, as: Mod defimpl Cache, for: Mod do def put(%Mod{redis: redis},x) do {:ok,1} = Redis.set redis, x, 1 end def cached?(%Mod{redis: redis},x) do {:ok,x} = Redis.get(redis,x) x != nil end end end # usage defmodule UsesCache do def start(cache) do Cache.put(cache,:hello) true = Cache.cached?(cache,:hello) end end UsesCache.start(%CacheMemory{set:HashSet.new}) ``` ## Passing callbacks For a single method, you could just pass a function. Then in tests you pass a stub method, and in production you can wrap up the real module behind it. ```elixir def start_link({some_callback}) do :gen_server.start_link(@name,SomeModule,{some_callback},[]) end def init({some_callback}) do {:ok,%State{callback: some_callback}) end ``` Now the `callback` field of state can be used by functions of this `gen_server` module. ```elixir # usage defmodule UsesCache do def start(put,cached) do put.(:hello) true = cached.(:hello) end end # can create callbacks from anything: stateful, stateless etc ``` + works with any implementation, even ad-hoc + dialyze-able - pass many values for large APIs ## Stateful Create a stateful module that holds the module, [refer to that](https://github.com/ericmj/hex_web/blob/84516f0320aa4c4840054831666a3f5d5958a9d6/lib/hex_web/router.ex#L29). - doesn't work for use-cases with many instances (pass-the-pid) + dialyzer - need a Behaviour for the return type of the getter ## Module generation You could [generate](https://gist.github.com/alco/c08256ae72d31c76e767) a module based on a run-time config. - moving parts - doesn't work for use-cases with many instances (pass-the-pid) - dialyzer - seems likely to throw it off