Java 模式裏面有一個概念叫「對修改關閉, 對擴展開放」, 這是一個面向對象的組件可重用原則. 裝飾器就是實現該原則的一個經典實例.git
經過符號註解的方式, 給被註解的函數或對象添加新功能, 重寫現有的功能, 而又不對現有的代碼作變化的一種方法. 它對使用者是透明的. 經過裝飾器能夠實現的經常使用功能包括:github
訪問控制框架
計時器探針, 檢測函數的運行時間函數
日誌記錄debug
在Elixir中, 主要是對函數進行裝飾. 一個函數裝飾器是形似@decorate
的一個符號註解, 緊接着函數定義的上一行, 它能夠用於給Elixir函數添加額外的功能. 函數裝飾器運行時開銷爲0, 由於它是在編譯時執行的.日誌
裝飾器以函數做爲參數, 而且返回一個通過修改, 或添加了新功能的函數. 它是一個高階函數.code
defmodule MyModule do use PrintDecorator @decorate print() def square(a) do a * a end end
函數裝飾器其實是Elixir宏.對象
Elixir 中的函數裝飾器, 咱們用到了 decorator 這個庫.get
定義裝飾器是比較簡單的, 建立一個模塊, 而且在模塊中 use Decorator.Define, [print: 0]
, 這樣就定義了一個名稱爲print
的裝飾器了. string
下面是一個裝飾器的完整定義示例:
defmodule PrintDecorator do # 聲明裝飾器的名號, 參數數量 use Decorator.Define, [print: 0] # 實現裝飾器 def print(body, context) do quote do IO.puts("Function called: " <> Atom.to_string(unquote(context.name))) unquote(body) end end end
裝飾器函數的參數(def print(...)
) 爲函數體(AST, 抽象語法樹), 以及一個context
參數, context
持有函數名稱, 定義模塊, 參數數量, 以及參數AST等信息.
裝飾器能夠進行編譯時參數傳遞, 例如日誌模塊僅打印消息級別爲:debug
的日誌.
@decorate print(:debug) def foo() do ...
對此, 須要修改裝飾器模塊的定義:
defmodule PrintDecorator do use Decorator.Define, [print: 1] def print(level, body, context) do # ... end end
除了傳入裝飾器函數的函數體AST
, 裝飾器函數還有一個傳入的上下文參數 context
, context
參數包含了函數調用的相關信息:
def print(body, context) do Logger.debug("Function #{context.name}/#{context.arity} called in module #{context.module}!" end
上下文具體包含哪些東西, 可經過
IO.puts #{inspect context}
打印出來.
下面是一個有用的示例, 是一個Phoenix框架中用戶檢查用戶認證的一個裝飾器宏.
defmodule Auth do def is_authorized(body, %{args: [conn, _params]}) do quote do if unquote(conn).assigns.user do unquote(body) else unquote(conn) |> send_resp(401, "unauthorized") |> halt() end end end end
~完 (^_^!)