2014年8月17日 星期日

一秒看破 C# Attribute

...我們絕非從未謀面

...總是悄悄的守候在我們身旁

...默默犧牲奉獻幫我們做了許多事

他是我們 最熟悉的陌生人

~ C# Attribute ~





此屬性 (Attribute) 非彼屬性 (Property)

對岸管他叫 "特性"

這裡還是叫他 Attribute 吧

M$ 的說明:

C# 提供了一個定義宣告式標記 (Tag) 的機制,此宣告標記稱為屬性 (Attribute),可以讓您在原始程式碼中放置一些實體,來指定額外的資訊。屬性所包含的資訊可以在 Run Time 時透過反映來擷取。您可以使用預先定義屬性 (Attribute)或者您可以定義自己的自訂屬性 http://msdn.microsoft.com/zh-tw/library/aa288059(v=vs.71).aspx





我們無時無刻都能瞥見 Attribute 的身影

加上 Attribute 我們的程式就被賦予了超能力

但是...Why?

為什麼明明我們什麼都沒做 只是加上 Attribute 程式就會多出一些能力呢?





答案顯而易見 自然是冥冥之中有股神秘的力量 Framework 幫我們完成了這些事

在 C# 物件導向 裡面通常都是靠 [繼承] 承接或擴充功能

但是[繼承]是一種垂直鏈狀的概念

當[繼承]深度越深的時候[可維護性]相對的就會變低(因為太多層了)

所以有句口訣叫做 多用合成 少用繼承

設法把不同功能拆分成 單一職責(Single Responsibility Principle)的小類別

再合體成模組級別的大類別 (怎麼合體自然也有一堆花招 EX:DI、IOC etc)

但是這僅止於 設計類別時內部自嗨的層面

有的時候需求來自是外部的





例如有個[顧客]類別有其自身的屬性方法

另外還有[店家]類別

[店家]類別自然無權干涉[顧客]類別的內部行為

理所當然[店家]對[顧客]提供服務

即便[店家]對外宣稱:我們無分男女、宗教、種族、階級、黨派,在服務上一律平等

但是用膝蓋骨想就知道那肯定是ㄊㄇ唬爛的

所以[店家]類別需要針對不同的[顧客]提供不同等級的服務

或許有人覺得那就給[顧客]一個叫做[顧客等級]的 Property 就能解決了

事實上的確可行 但是卻不是很合理

因為 Property 是屬於類別內部擁有的資訊 由類別決定如何提供給外界使用

如果[顧客]可以任意決定自身的[顧客等級]那未免也太可笑

這個時候應該使用 Attribute

Attribute 主要是給外部使用的資訊 LIKE 設定 貼標籤 註解 表述

以便[店家](外部)在使用[顧客](貼 Attribute 的類別)的時候能夠達成[提供不同等級的服務]的需求





講了那麼多 我想還是沒人懂 Attribute 到底在玩什麼把戲 (因為這就是 Attribute 的魅力特色呀 <3)

馬上來玩玩就對了





首先有兩個頁面 External 跟 Internal

分別使用 ExternalBulletin 跟 InternalBulletin

共用的部分繼承抽象類 Bulletin

Bulletin 在建構子 會使用 ArticlesFactory 來產生文章

Articles POPO 類 單純 DTO

如今響應個資法 需要 Articles 裡面的人名都改成 OO

這是一個外部因素帶來的改變 實際上 Articles 的本質不應該被改變 只是呈現的時候 人名必須要變成 OO

所以這裡設計一個 SilencerAttribute

裡面有個 SilencerMode 用來決定模式

現在可以給 ExternalBulletin 或 InternalBulletin 掛上 Attribute 了

基本上要掛哪個都可以(水平特性)

只是情境上是 外部的 Bulletin 需要保密

而內部的 Bulletin 則需要看到原始 Articles

只是掛上 Attribute 會怎樣嗎? 會有什麼事發生嗎??

用肋骨想就知道 當然不會怎麼樣 什麼鬼事也不會發生(鬼月的話就不敢保證)

Attribute 只是掛好看的標籤 自然是需要冥冥之的股神秘力量額外的類別來看這些 Attribute 做對應的變化 才會有效果

聰明的看官想必也早知道謎底了 就是 ArticlesFactory 要來幹這件事(因為[工廠]就是要[黑箱]作業呀)

這裡就能看到 ArticlesFactory 透過神奇的演算法(?) 巧妙的過濾人名 改成OO

結果顯而易見 就是 Articles 被改成OO 沒啥好講的





現在就能明白什麼是[水平擴充]了

水平層面上的 [類別] 或 [方法] 都能任意的掛上 Attribute 來擴充功能

抽換 Attribute 都不會影響該 [類別] 或 [方法] 內部的運作

但是對於 外部(會來看這些 Attribute 的 [類別] 或 [方法])來說 又能 [提供不同等級的服務]





這貨很神呀 根本可以 單排上金 的感覺 沒有缺點嗎?





缺點自然是有的

使用 Attribute 難以偵錯 因為你可能不知道 到底是誰在使用這些 Attribute

像是這個範例 是由 ArticlesFactory 來使用 Attribute 做判斷

如果並不清楚 ArticlesFactory 會使用 Attribute 的情況下

得到的 Articles 就會不如預期

可能就無法得知 到底為什麼 人名變成 OO 了

明明 CODE (垂直) 裡面都沒有寫到這段功能

這個情況在 分別開發的時候 會更顯著





那什麼時候適合使用 Attribute 呢?

.NET MVC 就是很好的示範了

Authorize HttpPost AllowHtml OutputCache

這些不影響 Controller 內部邏輯的功能 就能抽出來變成 Attribute 交由第三方 (Framework) 完成實作





最後 Attribute 還有個屌炸天的應用

極度推薦!