Zope 3 手冊/介面
介面是指定(文件化)“提供”它們的那些物件的外部行為的物件。 介面透過以下方式指定行為:
- docstring 中的非正式文件
- 屬性定義
- 不變式,即提供介面的物件必須滿足的條件
使用介面的一些動機是:
- 透過開發小型、可互換的片段來避免單塊設計
- 對外部責任、功能和行為進行建模
- 在功能片段之間建立契約
- 記錄 API
經典軟體工程書籍 設計模式(由“四人幫”編寫)建議您面向介面程式設計,而不是面向實現程式設計。 定義正式介面有助於理解系統。 此外,介面為您帶來了 Zope 元件架構的所有好處。
在一些現代程式語言中:Java、C#、VB.NET 等,介面是語言的顯式方面。 由於 Python 缺乏介面,Zope 將它們實現為一個元類,您可以從該元類繼承。
- "我可以做 X"
- 描述做某事的能力是 API 的經典定義。 這些能力被定義並實現為方法。
- "我擁有 X"
- 此語句宣告資料的可用性,這與模式經典相關聯。 資料儲存在屬性和特性中。
- "您可以用我做 X"
- 這裡我們描述了物件的行為。 通常沒有類似物。 但是,MIME 型別是行為宣告的絕佳示例。 這是使用空的“標記介面”實現的,因為它們描述了隱式行為。
這三種契約型別的區別首先由 Philipp von Weitershausen 以這種形式指出。
理解這些區別非常重要,因為其他程式語言不一定使用所有這三種概念。 事實上,通常只使用第一個概念。
- Python 沒有介面的概念
- 不是問題
- 介面只是物件
- "濫用" 類語句來建立一個介面
- PEP 245 中提出的語法
Jim Fulton 並不認為這是一個問題,因為它使介面成為一等公民。 例如,在 Java 中,介面是隻能在其預期的有限範圍內充當介面的特殊型別的物件。
另一方面,來自 zope.interface 包的介面透過實現元類(Python 的核心概念)來定義介面。 因此,介面只是在使用現有的 Python 模式。
這是一個經典的“你好世界”風格的例子
>>> class Host(object):
...
... def goodmorning(self, name):
... """Say good morning to guests"""
...
... return "Good morning, %s!" % name
在上面的類中,您定義了一個 goodmorning 方法。 如果您從使用此類建立的物件呼叫 goodmorning 方法,它將返回“早上好,...”!
>>> host = Host()
>>> host.goodmorning('Jack')
'Good morning, Jack!'
這裡 host 是您的程式碼實際使用的物件。 如果您想檢查實現細節,您需要訪問 Host 類,可以透過原始碼或 API 文件工具進行訪問。
現在我們將開始使用 Zope 介面。 對於上面給出的類,您可以像這樣指定介面
>>> from zope.interface import Interface
>>> class IHost(Interface):
...
... def goodmorning(guest):
... """Say good morning to guest"""
如您所見,該介面繼承自 zope.interface.Interface。 這種對 Python 類語句的“濫用”是 Zope 定義介面的方式。 介面名稱的 I 字首是一個有用的約定。
您已經在上一節中看到了如何使用 zope.interface 宣告介面。 本節將詳細解釋這些概念。
考慮這個示例介面
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> class IHost(Interface):
... """A host object"""
...
... name = Attribute("""Name of host""")
...
... def goodmorning(guest):
... """Say good morning to guest"""
介面 IHost 有兩個屬性,name 和 goodmorning。 回想一下,至少在 Python 中,方法也是類的屬性。 name 屬性使用 zope.interface.Attribute 類定義。 當您將 name 屬性新增到 IHost 介面時,您不會設定初始值。 在這裡定義 name 屬性的目的是僅僅表明該介面的任何實現都將具有一個名為 name 的屬性。 在這種情況下,您甚至沒有說明它必須是什麼型別的屬性! 您可以將文件字串作為第一個引數傳遞給 Attribute。
另一個屬性 goodmorning 是使用函式定義定義的方法。 請注意,介面中不需要 self,因為 self 是類的一個實現細節。 例如,模組可以實現此介面。 如果模組實現了此介面,將定義一個 name 屬性和一個 goodmorning 函式。 並且 goodmorning 函式將接受一個引數。
現在您將看到如何連線介面-類-物件。 因此,物件是真正存在的實體,物件是類的例項。 並且介面是物件的實際定義,因此類只是實現細節。 這就是為什麼您應該面向介面程式設計,而不是面向實現程式設計。
現在您應該熟悉另外兩個術語來理解其他概念。 第一個是“提供”,另一個是“實現”。 物件提供介面,而類實現介面。 換句話說,物件提供了其類實現的介面。 在上面的示例中,host(物件)提供 IHost(介面),而 Host(類)實現 IHost(介面)。 一個物件可以提供多個介面; 同樣,一個類可以實現多個介面。 除了其類實現的內容之外,物件也可以直接提供介面。
- 注意
- 類是物件的實現細節。 在 Python 中,類是可呼叫物件,那麼為什麼其他可呼叫物件不能實現介面呢? 是的,這是可能的。 對於任何可呼叫物件,您可以透過說明可呼叫物件實現了介面來宣告它會生成提供某些介面的物件。 可呼叫物件通常被稱為“工廠”。 由於函式是可呼叫物件,因此函式可以是介面的實現者。
要宣告一個類實現特定介面,請在類語句中使用 zope.interface.implements 函式。
考慮這個例子,這裡 Host 實現 IHost
>>> from zope.interface import implements
>>> class Host(object):
...
... implements(IHost)
...
... name = u''
...
... def goodmorning(self, guest):
... """Say good morning to guest"""
...
... return "Good morning, %s!" % guest
- 注意
- 如果您想知道 implements 函式是如何工作的,請參閱 James Henstridge 的部落格文章 (http://blogs.gnome.org/jamesh/2005/09/08/python-class-advisors/) 。 在介面卡部分,您將看到一個 adapts 函式,它的工作原理也類似。
由於 Host 實現了 IHost,因此 Host 的例項提供 IHost。 有些實用程式方法可以內省宣告。 宣告也可以在類之外編寫。 如果您沒有在上面的示例中編寫 interface.implements(IHost),那麼在定義類語句之後,您可以像這樣編寫
>>> from zope.interface import classImplements
>>> classImplements(Host, IHost)
介面可用於宣告特定物件屬於特殊型別。 沒有屬性或方法的介面稱為標記介面。
這是一個標記介面
>>> from zope.interface import Interface
>>> class ISpecialGuest(Interface):
... """A special guest"""
此介面可用於宣告物件是特邀嘉賓。