免责声明:我将您的“运行时配置”更多地解释为参数;如果不是这样,这个答案可能不是很有用。
Module 类似于 Class。
不幸的是,这种常见的 O-O 方法还不够相似; Elixir/Erlang 模块中没有“生命”,只是平面逻辑。您有效地尝试做的是将状态存储在模块本身中;在函数式语言中,状态必须保存在变量中,因为模块在所有进程的所有调用者之间共享——另一个进程可能需要存储不同的状态!
不过,这是一个常见的编程问题,在 Elixir 中有一种惯用的方法来解决它:GenServer。
如果您不熟悉 OTP,那么您应该学习它:它会改变您对编程的看法,它会帮助您编写更好(阅读:更可靠)的软件,并且它会让你开心。真的。
我会将配置存储在 GenServer 的状态中;如果您创建一个内部结构来表示它,您可以轻松地传递它并设置默认值;我们想要的所有东西都在一个令人愉悦的 API 中。
一个示例实现:
defmodule WebRequestor do
use GenServer
### API ###
# these functions execute in the CALLER's process
def start_link() do
GenServer.start_link(__MODULE__, [], [name: __MODULE__])
end
def start do
# could use .call if you need synch, but then you'd want to look at
# delayed reply genserver calls, which is an advanced usage
GenServer.cast(__MODULE__, :start)
end
#could add other methods for enabling debug, setting secrets, etc.
def update_url(new_url) do
GenServer.call(__MODULE__, {:update_url, new_url})
end
defmodule State do
@doc false
defstruct [
url: "http://api.my.default",
secret: "mybadpassword",
debug: false,
status: :ready, # or whatever else you might need
]
end
### GenServer Callbacks ###
# These functions execute in the SERVER's process
def init([]) do
config = read_my_config_file
{:ok, config}
end
def handle_cast(:start, %{status: :ready} = state) do
if config.debug, do: IO.puts "Starting"
make_request(state.url)
{:noreply, %{state|status :running}}
end
def handle_cast(:state, state) do
#already running, so don't start again.
{:noreply, state}
end
def handle_call({:update_url, new_url}, _from, state) do
{:reply, :ok, %{state|url: new_url}}
end
### Internal Calls ###
defp make_request(config) do
# whatever you do here...
end
defp read_my_config_file do
# config reading...
%State{}
end
end