在前篇文章裡模擬一個小型專案的需求,利用Paste的paster工具,在src目錄裡建立骨架程式碼,新增一個mysite.eventfolder的Archetypes模組專案,執行paster addcontent contenttype之類的指令後,則可以新增型別裡的欄位資訊。熟練的話,數分鐘之內就可以搞定內容型別的雛型。為了知行合一,這裡將逐步探究程式碼的細節,了解它們的原理跟修改方法。
但想要存取介面的屬性值,不能直接使用IEventFolder.
startDate的形式,因為會造成AttributeError的錯誤,這也是和Python物件不同的地方,如圖4所示。
|
▲圖4 介面屬性值的存取範例。 |
有了介面之後,ZCA讓interface-class-object三者之間產生新的關係,也就是說,object是class的instance,class實作interface的細節,ZCA並利用provide和implement這兩個技術動詞,來描述它們之間的相互關係。
例如,object有provide某個interface,class有implement某個interface,而且一個object能provide數個interface,一個class可implement數個interface,甚至class沒有implement的interface,它的object也可以直接provide這個interface。
宣告介面的實作
請留意,介面的定義值並不是class實作的內容,而是class實作的契約內容,同時,它也侷限屬性值的範圍,並搭配預設的驗證方法。
當介面完成定義後,如果有class宣告要實作這個介面,測試階段的程式碼已經可以執行,而不需要等到class真的把實作細節完成。
那麼,class該如何宣告要實作一個介面呢?它必須從zope.interface載入implements函式,在class裡執行implements(IEventFolder)之類的指令,如圖5所示。
|
▲圖5 宣告介面實作的範例。 |
由於EventFolder類別有implement介面IEventFolder,EventFolder類別的instance就會provide介面IEventFolder,而且在類別定義裡看得到相關的實作細節。
以上是實作一個介面的例子,實作多個介面時,可使用implements(IEventFolder, IOtherInterface)之類的語法,也就是用逗號(,)隔開即可。
值得一提的是,宣告介面實作的動作,也可以在類別定義之外指定,也就是事後使用圖6所示的語法範例來動態指定。
|
▲圖6 動態宣告介面實作的範例。 |
Archetypes的實作
以Archetypes的實作為例,它的具體內容同樣是一段class程式碼,位於content目錄裡,比對content/eventfolder.py和圖1裡的interfaces/eventfolder.py內容,會發現兩者有對應關係,如圖7所示。
|
▲圖7 Event Folder的欄位實作範例。 |
不過,Archetypes實作的程式碼並不會參照介面的定義。換句話說,即使class IEventFolder(Interface)底下空空如也,並不會影響Archetypes的實作。
另外,完全沒有屬性值的介面檔案,是最簡化的形式,又被稱為marker interface(圖8)。
|
▲圖8 marker interface的範例內容。 |
另外,也可以使用providedBy()來查詢某個物件是否provide某個介面,或是用implementedBy()查詢某個類別implement哪些介面,如圖9所示。
|
▲圖9 查詢物件類別介面三者的關係。 |
型別的編輯與顯示
存取介面資訊後,Archetypes會讀取content目錄裡的資訊。首先是content/configure.zcml檔案,內容包括型別的class定義檔位置、介面檔位置,還有權限資訊,如圖10所示。
|
▲圖10 content目錄的configure.zcml範例。 |
在content/eventfolder.py檔案裡,除了field和widget的EventFolderSchema定義細節外,關鍵內容就是class EventFolder(folder.ATFolder)的定義,以及implements(IEventFolder)的宣告,如圖11所示。
|
▲圖11 content目錄的eventfolder.py範例。 |