4

これは、シミュレーションモデルに使用するクラス「階層」の一部です(私のコードはPythonですが、私の質問は言語に依存しないと思います)。

class World:
# highest-level class, which "knows" everything about the model
# most likely will have just one instance
# contains (e.g., in a dictionary) references to all the instances of class Agent

class Agent:
# each instance represents an agent
# an agent can, among other things, move around according to certain rules
# movement depends on the internal state of the agent,
# but also on the terrain and other information not stored in the Agent instance

質問:moveインスタンスメソッドはどこに置くべきですか?

class Agentの依存関係を、それ自体よりも階層の下位にあるクラス(つまり、インスタンスがインスタンスに含まれているクラス)に制限することになっていると思いましたAgent。しかし、それは、地形などを記述するクラス(少なくとものインターフェイス)への依存関係を作成するため、moveメソッドを含めることができないことを意味します-したがって、への参照(したがって依存関係)を追加したほうがよいでしょう。これはソフトウェア設計の観点からは大丈夫ですか?class AgentAgentWorld

move別の方法は、メソッドをに配置することです。このclass World場合、追加の依存関係は発生しません。ただし、class Worldほとんどすべての作業を行うことになり、OOPの主なアイデア(すべての機能を1つの場所にまとめるのではなく、関連するクラスに含めることを理解しています)に反するように思われます。 。

パフォーマンスの考慮事項はわずかな懸念事項にすぎません(とにかく、パフォーマンスが2つのアプローチ間で異なるとは思いません)。

編集:上記の「クラス階層」という言葉を誤用しました。私は継承階層について言及しているのではなく、インスタンスが相互に含まれている一連のクラスについて言及していました。

4

5 に答える 5

3

考慮に入れる必要があるのは、単一責任の原則です。基本的に、各クラスは1つの「もの」に責任を負い、その1つの責任を完全にカプセル化する必要があります。そして、あなたは責任が拡大されたところだけを継承するべきです。拡張クラスは親の100%以上(特定の意味ではもっと)であると常に言えるはずです。子が親のサブセットであり、「少ない」という状況は決してありません。したがって、人とは関係のない世界の側面があるため、世界を拡張する人は良いデザインではありません。

したがって、例を見ると、その特定のクラスの役割によって指定されるレベルにインスタンスメソッドを配置することになります。それでは、例をより明確に見てみましょう。

class Person:
    name: ""
    birthDate: ""

class PoliceOfficer extends Person:
    badgeNumber: ""

明らかにこれは擬似コードですが、何が起こっているかを示しています。

では、どこにメソッドを追加しmove()ますか?に追加することもできますが、人も移動できるためPoliceOfficer、カプセル化を解除します。Person

class Person:
    def move(world):

しかし、どこにissueTicket()メソッドを追加しますか?一般人Personはチケットを発行できないので、それをPersonクラスに追加すると、その責任を破ることになります。代わりに、それをに追加しPoliceOfficerます。それが理にかなっているからです。

依存関係を作成する限り、継承よりもコンポジションを常に優先する必要があります。したがって、その意味では、依存関係はすべてソフト依存関係であるため、必要な数だけ依存関係が存在する可能性があります(まあ、一種の)。move()のインスタンスworld(またはワールドインターフェイスを持つオブジェクト)を取得するため、依存関係はクラスから呼び出し元のコードにプッシュされます。これにより、生産性を維持しながら、クラスのコードをかなりオープンで依存関係のない状態に保つことができます。

一般に、依存関係をハードコーディングすることは悪い習慣と見なされています。しかし、それらを(依存性注入または構成を介して)注入することは、通常、良いことと見なされます。

要約:インスタンスメソッドを配置することが論理的に意味のある場所に配置します。

于 2011-02-01T20:47:42.687 に答える
1

移動方法を意味のある場所に置きます。世界は移動できません。エージェントは移動できます。

ワールドレベルの機能にアクセスできるようにしたい場合は、コンストラクターメソッドにパラメーターを指定して、ワールドインスタンスをに渡します。

world = World()
agent = Agent(world)

これにより、ある種の階層を想定するのではなく、エージェントからワールドへの明示的なアクセスが可能になります。

これをさらに一歩進めて、ワールド内のすべてのゲームオブジェクトがワールドをパラメーターとして受け取るように要求できます。これを強制するには、エージェントや他のゲームオブジェクトが継承する基本のGameObjectクラスを作成します。

class GameObject:
    def __init__(self, world):
        self.world = world

class Agent(GameObject):
    def __init__(self, world, startX, startY):
        # don't forget to call the super and pass the world to it
        super(Agent, self).__init__(world)
        self.startX = startX
        self.startY = startY

    def move(self):
        print 'I can see the world'
        print self.world

編集:私の説明をさらに拡張するために、あなたがEnemyクラスを持っていて、敵にもmove()方法があった場合、あなたは敵をエージェントに向かって動かしたいと思うかもしれません。ただし、敵がエージェントの位置を世界に尋ねるのは望ましくありません。代わりに、敵の内部にあるエージェントへの参照を「ターゲット」として保存し、必要なときにいつでもその位置を確認できます。

class Enemy(GameObject):
    def __init__(self, world, target):
        super(Agent, self).__init__(world)
        self.target = target

    def move(self):
        if self.target.x > self.x:
            self.x += 5
于 2011-02-01T09:20:49.350 に答える
1

私はmoveクラスに入れAgentます。必要がAgentなければ、全世界を知る必要はありませんが、彼が移動する必要があるのは関連情報だけです。しかし、彼が全世界を知っていれば、それも悪くはありません。ここにいくつかの理由があります:

  1. moveメソッドをWorldクラスに配置する場合、単一のエージェントをどのように移動しますか?のインスタンスAgentをそのメソッドに移動しますか?それはかなり醜いようです。
  2. 単一のエージェントに何かを実行させたい場合は、OOPの観点からインスタンスメソッドで実行する方が適切です。
  3. また、ワールドを認識していないが特定のエージェントを認識している別のクラスのインスタンスからmoveメソッドを呼び出すこともできます。

ただし、すべてのエージェントが同時に移動し、単一のエージェントを移動させたくない場合は、1つのメソッドmoveAllAgentsをワールドクラスに入れてから、エージェントのリストを反復処理して、これらすべてのエージェントを移動できます。その場合、クラスにmoveメソッドは必要ありません。Agent

于 2011-02-01T09:23:58.247 に答える
0

ただし、moveメソッドは、地形などを記述するクラス(少なくとものインターフェイス)への依存関係を作成するため、クラスAgentに含めることはできません。

エージェントは自分の周囲について知っている必要があります。つまり、エージェントは地形を表すインターフェースを使用する必要があります。しかし、私はそれを「依存関係」とは呼びません。ITerrainを実装するクラスが実際にITerrainインターフェースに従っていると仮定しても問題はありません。:-)

したがって、エージェントに.move()を配置します。次に、move()メソッドは周囲をチェックし、移動のルールに従って周囲を移動しようとします。

于 2011-02-01T09:43:06.023 に答える
0

アプリケーションについてあまり知らない場合は、エージェントが世界の特定の部分について「知る」ことを許可することもできます(おそらく、エージェントが実行できる範囲について実装するいくつかのルールによって制約され、移動を実行できる最大領域として定義されます)。 .Moveへの1回の呼び出しで移動します)。したがって、エージェントには、より大きな世界の「クリップ領域」への参照が含まれている可能性があります(この概念は、.net GDI +グラフィックオブジェクトで使用される「クリップ長方形」から盗まれました)。

より一般的な意味で、私は他の人に同意します。Moveメソッドがエージェントクラスで定義されることは完全に理にかなっており、エージェントクラスが周囲を認識していることは許容されます。

OOPは不必要な依存関係を最小限に抑える傾向がありますが、それが理にかなっている場合は理にかなっています。現実世界の人は自分の周囲を認識しており、ある場所から別の場所にそれらの周囲内を移動するために必要なアクションを開始することができます。

于 2011-02-01T14:46:58.130 に答える