annotation 的二三事

第一次看這隻小老鼠

在 java 裡面常常會看見下面這樣的用法

@Override
void method() {
}

上面有個小老鼠的 @Override 便是 annotation 。
這個 @Override 的作用是開發者繼承某個 class 之後,想要覆寫父類別的時候加上這個 annotation 可以讓編譯器知道你想覆寫,因此編譯器會檢查父類別是否有相同的 method ,以免你原本想要覆寫父類別的 method 結果卻手殘打錯字沒有真的覆寫到而發生意外。

因此 annotation 的作用就是讓被 annotation 標籤上的 class, method 之類的東西額外的增加資訊。

再看小老鼠

一開始我也不知道這 annotation 到底有什麼用途,直到我看見 python 的幾個 web 框架有以下的做法

@get('/index')
def getIndex():
    return "<p> hello </p>"

這是個小小的 route ,說明的是如果使用者使用 GET 方法連線到 /index 這個網址,就使用被標籤的 method ,而這個被標籤的 method 會回傳一串 html 作為回應,這裡不必太過在意後面的行為。

小老鼠進化史

從前面看來,發現似乎如此一來前面的 getIndex() 邏輯是很清晰的,而不會混雑著太長的設定路徑以及 http method 的邏輯,而是交由背景某個東西去運作,只需要使用 @get('/index') 這樣的標籤,就能讓背景的魔手去設定。

而這樣的概念爬了一下歷史,可以發現似乎是在 C 時代就會想使用 macro 去改變 function 的宣告,像是在 php 可以常常看到像是下面這樣的方法去宣告 function

// hello.c
PHP_FUNCTION (hello) {
    // do something
}

而當中的 PHP_FUNCTION 是個 macro 會去將這整個文字內容改成符合自己要求的 function 結構,隨便瞎掰一個可能的例子,例如:

// hello.c
php_function_ret *hello (int a, int b) {
    // do something
}

這樣可以將這段可重用的邏輯抽離出來並且隱藏又臭又長的 code ,而且如此一來還有一點點這是給 hello 這個 function 進行撰寫程式內的設定檔的概念

因此可以稍微整理出我們對此類的需求大概如下:

  • meta:想將使用者的程式進行設定,或者標記上某些屬性
  • 改變流程: 執行使用者的內容前後,想添加某些程式去執行

回頭看看 java annotation

因此針對第一點,java 提出了 annotation 的概念,但是僅能加上屬性不能改變被標記程式的流程。
所以當初 java 這點被廣泛的應用拿來生成文件以及生成設定檔。 不過在這個流程反轉的年代, java annotation 並沒有因為只能作為 meta 而失去威力。 只要是有套上框架的程式,流程一開始都是交由框架去跑,所以套上框架的程式只要在編譯或是建置的時候,掃描所有 code 裡有 annotation 的地方,便能影響框架使之把控制權交給你,便能演出剛剛 python 裡的 @get('/index') 精彩戲碼

來看看 python decorator

在來看看 python 的小老鼠,其實是與 java annotation 有所不同,以往這種想對程式添加東西的動作,在設計模式裡發展出了一個叫做裝飾者模式的東西,使這種添加的動作不會太過難看,所以 python 直接使用 decorator 作為命名啦。

寫一個小小的範例如下

def log(func):
    print("func name:", func.__name__)
    return func

@log
def hello():
    print("hello")

hello()

在此可以看到我寫了一個 log 的 function ,這個功能只是把被 log 標記過的 function ,在被呼叫的時候記住一下他的名字。

在這裡的呼叫 hello()時其實它的功能就是轉換成如下面這般形式

log(hello)()

是請 log 加工一下我的 hello function ,還給我一個加工過的 hello function ,而透過 decorator 可以把這醜陋的加工弄得漂亮一些。 而 code 可以很專注在自己的宣告上面,而 log 就變成了第二級需要注意的資訊,對於開發者大概就知道「喔,大概是要讓這個 function 被紀錄一下吧」,可以很明顯的看到能將眼睛的注意力分為兩種不同的等級。

小結論

有些人會覺得這種小小的 meta 功能根本不足以說嘴,但我覺得,多了 annotation 或是 decorator 其實能夠讓程式的閱讀性提升,而且可以少額外寫出一個設定檔的時間,因此我實在不覺得小老鼠是個應該被無視的功能呀 ~