Zope は内部の多くの箇所でコンポーネントアーキテクチャを使用しています。 Zope のコンポーネントとは単なる Python のオブジェクトで、インターフェース によってその特性が記述されているに過ぎません。 Zope の開発者となる あなたは、このインターフェースを使用して独自の Zope コンポーネントを 構築することが出来ます。
コンポーネントとは、インターフェースに結びつけられたオブジェクトの事です。 インターフェースとは、他の Python オブジェクトとどのように協調動作するのか が記述されている Python のオブジェクトです。 本章では、1つのインターフェースと、いくつかのシンプルなコンポーネントの 作成を通してそれらがどのように動作するかを見ていきます。
ここに挨拶するだけの非常にシンプルなコンポーネントがあります。 このように全てのコンポーネントはこの例のように一般的には2つの部品、 1つのインターフェースと1つの実装で構成されます:
from zope.interface import Interface
from zope.interface import implements
class IHello(Interface):
"""The Hello interface provides greetings."""
def hello(name):
"""Say hello to the name"""
class HelloComponent(object):
implements(IHello)
def hello(self, name):
return "hello %s!" % name
それでは1行ずつ見ていきましょう。まず2つの Python のクラスが 定義されています。最初のクラスは インターフェース を作成しており、 2つめのクラスは 実装 を作成しています。
最初のクラス定義は IHello インターフェースを作成しています。 このインターフェースは1つのメソッド hello を記述しています。 注意して欲しいのは、ここにはメソッドの実装は無く、インターフェース は動作を決めずに、単に仕様のみを記述していると言うことです。
2つめの class 定義では HelloComponent クラスを定義しています。 このクラスは IHello の記述が実際に何を 行う のかを定義した コンポーネントです。通常これを IHello の 実装 と言います。 ここで、 HelloComponent が何のインターフェースを実装しているのか という事をどうにかしてそのクラスとインターフェースとで表す必要があり、 クラス内で呼ばれている implements 関数がそれを行っています。 これは “このクラスはこれらのインターフェースを実装します” という意味になります。 この例の場合、 HelloComponent は1つのインターフェース IHello を実装していることを表明しています。
インターフェースはオブジェクトがどのような機能を持つかを記述しますが、 その実装がどのように行われるべきかを決めたりはしません。 例えば、ここにもう少し複雑な IHello の実装があります:
import xmlrpclib
class XMLRPCHello:
implementats(IHello)
def hello(self, name):
"""Delegates the hello call to a remote object
using XML-RPC.
"""
s = xmlrpclib.Server('http://www.zope.org/')
return s.hello(name)
このコンポーネントはリモートサーバーに問い合わせを行い、リモート コンポーネントから挨拶文を取得します。
これもまた一つのコンポーネントです。本章の残りの部分では、 インターフェースについてと、インターフェースとコンポーネントの協調動作 について説明します。3章の中で、これらを Zope プロジェクトに当てはめて 見ていきます。
インターフェースは、オブジェクトに関する役に立つ情報を含むことによって、 オブジェクトの動きについて説明します。役に立つ情報とは:
これらの情報全てが義務ではありません。例えば、作成したインターフェース のメソッドの説明ドキュメントは記載しても引数の説明は詳細には書かない といった事もできます。インターフェースオブジェクトはフレキシブルなので、 コンポーネントが何を取捨選択するかを決めることが出来ます。
インターフェースは、多くの開発者が参加する大きいシステムの開発で見られる、 いくつかの問題を解決します。
インターフェースは、オブジェクトの使い方を記述する方法を提供し、 記述された説明を見つける仕組みを提供することで、このような問題を 解決しようとしています。
コンポーネントを作成する最初のステップは、これまで見てきたように、 インターフェースを作ることです。
インターフェースオブジェクトは Python の class 構文で簡単に 構築することが出来ます。覚えておいて欲しい事として、 class 構文を使っているためすこし紛らわしいのですが、 インターフェースは class では ありません 。 Python のクラスの構文を使用しているのは単に便利だからであり、 結果として得られるオブジェクトはクラスではなく インターフェース だという事を理解しておくのは重要です。
Python のクラス構文を使用してインターフェースを作成するには、 zope.interface.Interface を継承した Python クラスを作成して ください:
from zope.interface import Interface
class IHello(Interface):
def hello(name):
"""Say hello to the world"""
このインターフェースは、各メソッドの挙動を実装しておらず、ただ単に “IHello” オブジェクトが実装された場合のインターフェース仕様を 記述しています。 zope.interface.Interface を継承することにより、 得られる IHello オブジェクトはインターフェースオブジェクト になります。 Python インタープリタを使って以下のように確認出来ます:
>>> IHello
<InterfaceClass __main__.IHello>
ここで、利用者の挙動を定義した具象クラスと IHello インターフェース を関連づける事が出来ます。例えば:
class HelloComponent:
implements(IHello)
def hello(self, name):
return "Hello %s!" % name
この新しい HelloComponent クラスは IHello インターフェースを 実装した具象クラスです。クラスは1つ以上のインターフェースを持つこと も出来ます。例えば、 “Container” オブジェクト内でどのように動作するのか という事が記述された ‘IItem’ というインターフェースがあるとします。 もし HelloConponent インスタンスに IHello と同じように、 IItem インターフェースを実装することを宣言するのであれば、 ‘HelloComponent’ クラスの宣言で以下のように、インターフェースオブジェクト を並べて記載します:
class HelloComponent:
implements(IHello, IItem)
インターフェースは他のインターフェースに拡張できます。例えば、 IHello インターフェースを拡張してメソッドを追加してみましょう:
class ISmartHello(IHello):
"""A Hello object that remembers who it's greeted"""
def lastGreeted(self):
"""Returns the name of the last person greeted."""
ISmartHello は IHello インターフェースを拡張しています。 これは、クラス構文が他のクラスを継承するのと同じ構文で実現できます。
ここで、 ISmartHello に対して、自身が拡張しているインターフェース の一覧を getBases を使って問い合わせることが出来ます:
>>> ISmartHello.getBases()
(<InterfaceClass __main__.IHello>,)
インターフェースは複数のインターフェースから拡張することが出来、 getBases はそれらのインターフェースの一覧を返すでしょう。 ISmartHello があるインターフェースを拡張しているかどうかを 知りたい場合、 getBases を呼び出し、結果の一覧を検索して探す ことも出来ますが、 extends メソッドを呼び出せばより簡単に 目的の結果を得られます:
>>> ISmartHello.extends(IHello)
True
>>> ISandwich(Interface):
... pass
>>> ISmartHello.extends(ISandwich)
False
extends を使って、あるインターフェースが他のあるインターフェース を拡張しているのかどうかを判断出来る事が分かりました。
あなたはインターフェースが他のインターフェースを拡張することと、 クラスが他のクラスをサブクラス化することとに類似性を見いだすでしょう。 これらは同じコンセプトですが、この二つを同じと考えるべきではありません。 クラスとインターフェースが1対1に対応づけられているという前提はありません。 1つのクラスが複数のインターフェースを実装することもあるでしょうし、 あるクラスは基底クラスのインターフェースを実装しないかもしれません。
クラスとインターフェースとの区別は常に明確に保たれるべきです。 クラスの目的はオブジェクトがどのように動作するかについての実装を 共有することです。インターフェースの目的はオブジェクト と どのように 動作するのかを記載することであり、オブジェクトがどのように実装されるか を決めることではありません。これにより、同じインターフェースの実装 として全く異なる実装を持ついくつものクラスを持てるようになります。 このような違いがあるので、インターフェースとクラスを混同するべきでは ありません。
インターフェースは情報取得のための問い合わせの仕組みがあります。 最もシンプルなケースとしては、インターフェースを構成している全ての 要素名を問い合わせることがあります。例として Python インタープリタを 使って以下のようにインターフェースが持っている 名前 を問い合わせます:
>>> User.names()
['getUserName', 'getFavoriteColor', 'getPassword']
インターフェースは各要素についてさらに詳細な情報を取得する方法も 提供しています。インターフェースオブジェクトは、 namesAndDescriptions メソッドを使うことで各要素についての ‘(name, description)’ タプルの リストを返します。
例:
>>> User.namesAndDescriptions()
[('getUserName', <Interface.Method.Method object at 80f38f0>),
('getFavoriteColor', <Interface.Method.Method object at 80b24f0>),
('getPassword', <Interface.Method.Method object at 80fded8>)]
この例ではインターフェースの3つの要素の “description” は全て Method オブジェクトです。 ‘Attribute’ と Method のどちらも Description オブジェクトになることが出来ます。属性、メソッド、そしてインターフェース オブジェクトは以下のインターフェースを実装しています:
メソッドオブジェクトは Python メソッドについてのより詳細なメタデータ を記述する仕組みを提供しており、以下のメソッドを持っています:
例:
>>> m = User.namesAndDescriptions()[0][1]
>>> m
<Interface.Method.Method object at 80f38f0>
>>> m.getSignatureString()
'(fullName=1)'
>>> m.getSignatureInfo()
{'varargs': None, 'kwargs': None, 'optional': {'fullName': 1},
'required': (), 'positional': ('fullName',)}
getSignatureInfo を使うことでメソッドの各引数の名前や型について 知ることが出来ます。
クラスやインスタンスが、あるインターフェースを実装しているかどうかを、 インターフェースに問い合わせることが出来ます。 例えば、 HelloCmponent クラスが ‘IHello’ を実装しているかどうかは 以下のように確認します:
IHello.implementedBy(HelloComponent)
これは真を返します。同様に HelloComponent のインスタンスについても、 インターフェースに対して、インスタンスがそのインターフェースを実装 しているかを以下のように確認します:
IHello.implementedBy(my_hello_instance)
これにも、 my_hello_instance が HelloComponent のインスタンスならば 真を返します。他に IHello インターフェースを実装しているクラスであれば 真を返すでしょう。
インターフェースは、 Python オブジェクトの定義を記述するシンプルな 方法を提供します。インターフェースを使って、オブジェクトが出来る事 を記載してください。 Zope は今後もっとコンポーネント指向になっていき、 そのとき、あなたのオブジェクトも、よりフィットしてくるでしょう。 コンポーネントとインターフェースの技術が進み、ドキュメントと 検証の機能が今日ではとても使えるものになっています。
(Translated by Shimizukawa, r104989, original-site)