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