3

Pythonで「単純なスクリプトやものよりも大きく」アプリケーションを作成する際にいくつかの問題が発生しています。WinMainというクラス、Engineというクラス、Stateというクラス、Elementというクラスがあります。これは次のようにレイアウトされています。

WinMainはプログラムのメインクラスであり、mainLoop関数やその他のさまざまな機能を備えています。

エンジンは、画像やレンダリングコードなどのデータを含むオブジェクトです。

Stateは、Engineが保持するオブジェクトであり、Engineのstep関数が呼び出されるたびにupdateメソッドが呼び出されます。

要素は、レンダリングするGUIボタンや画像などのStateによって保持されるオブジェクトです。

私のレンダリング関数はエンジンにあるので、どのように要素(状態によって保持される)を取得してレンダリングするのですか?各要素にEngineのインスタンスを与えることができると思いますが、次のようなことをしなければならないので、それは一種のハッキーのようです:( picture = Element(engine, (0, 0), 'gui_image')良くない)

私はむしろやりたいのですがpicture = Element((0, 0), 'gui_image')、それでもElementにEngineを使用して物事をレンダリングさせる方法があります。

これは、私が開始するほとんどのプロジェクトで抱えている主要な構造上の問題のようであり、それを回避する方法を見つけることができないようです(クラスと関数の引数を介して大量の変数を渡す以外)。どうすればこれを行うことができますか?

4

3 に答える 3

4

エンジンは、画像やレンダリングコードなどのデータを含むオブジェクトです

これは神のクラスのように聞こえます。GUIコードでは一般的であり、発生している問題はその一般的な影響です。

Engineのさまざまなものの間の概念的な関係は何ですか?緊密に結合する必要がありますか、それともエンジンは州、レンダラー、その他のものの間で調整することができますか?

ただし、そのビットは機能しなくなり、エンジン(またはレンダラーなど)をステートマシンに渡すことが、現在のアーキテクチャでグローバル(またはシングルトン)を回避する唯一の方法です。


この依存関係を解消する通常の方法は、MVC(モデル/ビュー/コントローラー)パターンのようなものを使用することです。具体的には、エンジンはすでに大まかにコントローラーであり、状態と要素にはモデル部分が含まれているようです。ただし、エンジン、状態、および要素は、レンダリング/プレゼンテーションの部分も処理します。これにより、結合が導入されます。

状態の変更を一部のオブザーバーに公開する方法を見つけることができれば(状態ロジックがオブザーバーの詳細に過度に依存することを望まないため、意図的にあいまいなままにします)、レンダラーに状態をリッスンさせることができます(モデル)更新して描画します。これで、オブザーバーは好きなものにすることができるので、モックするのは簡単で、エンジンからも切り離されています。

于 2012-10-11T22:38:39.360 に答える
1

明らかなことは、各「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はあなたが求めていることを実行できたとしても、「エンジン」引数を渡す方が良いように聞こえます。

于 2012-10-11T22:39:34.347 に答える
0

Pythonを知らないので、構文が間違っている場合はすみませんが、一般的なOOの答えengineは、レンダリングを行う要素の関数にオブジェクトを渡すことです。

したがって、Engineのステップ関数には次のようなものがあります。

state.Update(this)      - so State gets a reference to the engine.

State必要に応じて、いつ作成されたかを参照することもできEngineます。

次に、state.Update()あなたがする方法で:

element.Render(engine)

これはおそらく、stateオブジェクトによって保持されている要素のコレクションを反復処理するループ内にあります。

于 2012-10-11T21:32:31.603 に答える