明らかなことは、各「Element」オブジェクトの属性として「Engine」を使用することです(または、renderメソッドを呼び出すときに値として渡されます)。そのためには、「State」オブジェクトが知っている必要があります。 Engineインスタンスを作成し、インスタンス化されたときに各Elementに渡すか、各renderメソッド呼び出しで渡します。これは問題のようです。
同様に、「状態」が「エンジン」を知るためには、作成時、またはレンデギング(「状態」)をトリガーするアクションの呼び出し時に、それについて知る必要があります。
Pythonの動的な性質には、毎回明示的にすべてを記述しなければならないことを軽減する方法があります-はい-しかし、yoiuは、コードを保守できないように、埋めすぎないように注意する必要があります。ここでいくつかの例に要約できるかどうか見てみましょう:
class WinMain(object):
def __init__(self):
self.engine = Engine()
def loop_once(self):
self.engine.step()
class Engine(object):
def __init__(self):
self.state = State()
def step(self):
self.state.update(self)
def render(self, obj):
print (obj)
class State(object):
def __init__(self):
self.element = Element()
def update(self, engine):
self.element.render(engine)
class Element(object):
def render(self, engine):
engine.render(self)
if __name__ == "__main__":
win = WinMain()
win.loop_once()
このコードは、更新時にのみEngineオブジェクトを渡します。これにより、Elementインスタンスが完全に異なるエンジンに属することができるため、エンジンを状態属性またはElement属性として固定するよりも興味深いものになります。また、「engine」パラメーターを要素の作成時間から切り離します。これは、Engineでボタリングしたくないと不満を言った部分であるため、煩雑さを軽減できます。
いずれにせよ、「要素」に「オブジェクト」について何らかの方法で認識させる必要があります。引数を明示的に渡さない場合は、暗黙的に行う必要があります(つまり、「エンジン」インスタンスを「状態」内で認識させる必要があります)。と「要素」はどういうわけか)。1つの方法は、インスタンス化時にフレームイントロスペクションを実行するベースクラスまたはメタクラスを記述し、呼び出し元のコンテキストの「self」変数を新しいオブジェクトの属性にバインドすることです。それを行うこの暗黙の方法はひどいにおいがします。:-)別の方法は、メソッドがナビゲートできるDOMインターフェイスを使用して、オブジェクトの関係を別のデータ構造に保持することです。ただし、とにかく関係に注釈を付けるために追加の呼び出しを行う必要があります(おそらく、追加の呼び出しは、上記のように内省的なベースクラスまたはメタクラスで行われる可能性があります)。
もう少し考えてみると、Pythonアプリケーションサーバー「zope」は「acquisition」を使用してこの問題を解決します。zopeアプリケーション内のすべてのオブジェクトはツリー内にあります。属性を持たないオブジェクトから属性を取得しようとすると(たとえば、要素の「render」メソッドを呼び出そうとすると、取得メカニズムは、メソッドを実行する親が見つかるまで、ツリー上でその属性を「上向き」に検索します。次に呼び出され、元のオブジェクトをパラメーターとして取得します。それはあなたにとって可能な解決策ですが、取得メカニズムを実装する基本クラスを必要とするだけでなく、
ケースを単純化するための単純な「取得」実装を次に示します。
class WinMain(object):
def __init__(self):
self.struct = []
self.engine = Engine()
def loop_once(self):
self.engine.step()
class TraverseError(AttributeError):
pass
class AquisitionBase(object):
tree = {}
def __getattr__(self, attr):
# __getattr__ is only invoked when the original object does not have
# attr by itself
try:
return getattr(self.__class__.tree[id(self)], attr)
except (IndexError, AttributeError), error:
raise TraverseError(error)
def __setattr__(self, attr, obj):
cls = self.__class__
# Anotate parent:
cls.tree[id(obj)] = self
return super(AquisitionBase,self).__setattr__(attr, obj)
class Engine(AquisitionBase):
def __init__(self):
self.state = State()
def step(self):
self.state.update()
def render(self, obj):
print (obj)
class State(AquisitionBase):
def __init__(self):
self.element = Element()
def update(self):
self.element.render(self.element)
class Element(AquisitionBase):
pass
if __name__ == "__main__":
win = WinMain()
win.loop_once()
たとえば、「要素」を含むリストまたはdictがある場合は、このスキームを認識しているlistおよびdictのサブクラスを作成し、基本的に__setitem__
の親に注釈を付けるメソッドを実装することで、コンテナーに関する問題を克服できることに注意してください。オブジェクトもそこに帰属します。
基本的に-とにかくそれについて考えるようになりますが、Pythonはあなたが求めていることを実行できたとしても、「エンジン」引数を渡す方が良いように聞こえます。