2

ユーザーがオブジェクトをグリッドに配置してゲームのレベルを作成できるようにする C#/Winforms のアプリケーションがあります。タイル/ライト/ドア/エンティティなどを配置するためのツールがいくつかあります。現在、現在選択されているツールを格納するために列挙型を使用し、各ツール コードを実行するための switch ステートメントを使用しています。アプリケーションにツールを追加していると、コードが重複してスパゲッティのようになり始めています。

これは、私のエディター クラスのマウス ダウン機能のカットダウン バージョンです。

    public void OnEditorViewMouseDown(Point mousePos)
    {
        // Check if the click is out of bounds.
        if (IsLocationOOB(mousePos)) return;

        if (CurrentTool == ToolType.GroundTile)
        {
            // Allow drags along whole tiles only.
            m_DragManager.DragType = DragManager.DragTypeEnum.Tile;
            m_DragManager.StartDrag(mousePos);
        }
        else if (CurrentTool == ToolType.WallTile)
        {
            // Allow drags along grid edges only.
            m_DragManager.DragType = DragManager.DragTypeEnum.Edge;
            m_DragManager.StartDrag(mousePos);
        }
        else if (CurrentTool == ToolType.PostTile)
        {
            // Allow drags along grid points only.
            m_DragManager.DragType = DragManager.DragTypeEnum.Point;
            m_DragManager.StartDrag(mousePos);
        }
        else if (CurrentTool == ToolType.AreaLight)
        {
            // Allow drags anywhere. ie. not snapped to the grid in some way.
            m_DragManager.DragType = DragManager.DragTypeEnum.FreeForm;
            m_DragManager.StartDrag(mousePos);
        }
        else if (CurrentTool == ToolType.PointLight)
        {
            m_CurrentWorld.AddLight(TranslateToWorldCoords(mousePos));
        }
        else if (CurrentTool == ToolType.PlaceEntity)
        {
            m_CurrentWorld.PlaceEntity(TranslateToWorldCoords(mousePos));
        }
    }

スイッチは他のいくつかの機能 (OnMouseMove、OnMouseUp) で使用されており、設計が悪いようです (いくつかの機能で大きなスイッチがコピーされています)。このようなものをよりクリーンで拡張可能な方法でリファクタリングするためのアドバイスはありますか? 私は現在、基本Toolクラスを持ち、各ツールが使用する関数 (OnMouseDown() など) をオーバーライドする独自のクラスを持つことを考えています。これは理にかなっていますか?

読んでくれてありがとう。

4

7 に答える 7

6

あなたは良い直感を持っています。

通常、OOP では、if や巨大なスイッチの行がある場合、それは強いコード臭です。この臭いをなくす最善の方法は、ポリモーフィズムを使用することです。

基本抽象クラス BaseTool を使用して、さまざまな OnXXX メソッドを nop として実装し (リターンするだけなので、ツールがメソッドで動作する方法を知っている場合にのみ動作を指定する必要があります)、アイデアを進める必要があります。ツールは BaseTool から継承し、関連するメソッドをオーバーライドして独自の動作を実装します。

だからあなたの方法は結局

public void OnEditorViewMouseDown(Point mousePos)
{
  currentTool.OnEditorViewMouseDown(mousePos);
}

設計によっては、DragManager をメソッドに渡すことも検討して、配置されているインスタンス変数に縛られないようにする必要があります。「グローバル」変数をフェッチすることなく、メソッドが必要とするすべてのものを備えた EditorContext (DragManager を含む) は、リファクタリング時にメソッドをより自己完結型にし、脆弱性を軽減します。デザイン自体は、誰が何を担当するかという責任に依存します。

于 2009-06-03T00:06:26.437 に答える
5

戦略パターンを使用するのに適した場所のように思えます: http://www.google.com/search?q=c%23+strategy+pattern

于 2009-06-03T00:16:56.087 に答える
3

そうです、すべてのツールで必要なすべての共通メソッドを定義する基本クラス (または少なくともインターフェイス) が絶対に必要です。このコードを機能させてみてください。そうすれば、クラスを設計する方法の良いアイデアが得られます:

m_DragManager.DragType = CurrentTool.DragType;
m_DragManager.StartDrag(mousePos);

ここで、「CurrentTool」は基本クラスまたはインターフェイスのインスタンスです。

基本的に、「ツール」が選択されると、その時点でどの派生ツールを扱っているかを判断しますが、その時点から、基本クラスのみを処理し、列挙型などを決定することを忘れます現在選択されているツール。わかる?

于 2009-06-03T00:04:13.257 に答える
3

はい、ここで必要なのはポリモーフィズムです。基本に実装を追加する必要があるかどうかに応じて、抽象基本クラス Tool またはインターフェイス ITool のいずれかを定義する必要があります (つまり、すべてのツールに共通の機能がある場合は、抽象基本クラスを使用できます)。

マネージャーは、何かを行う必要があるときにツールまたは ITool を使用する必要があります。ツールは、必要な情報を受け取り、返す必要があるものを返すか、必要な処理を実行する Drag 関数を実装します。または、マネージャーとツールの間で制御の反転を実装し、プロパティ インジェクション (Tool.Manager = dragManager) を介してマネージャーをツールに挿入し、マネージャーを使用して必要なことをツールに実行させることもできます。

于 2009-06-03T00:09:27.367 に答える
2

http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html

于 2009-06-03T00:06:07.483 に答える
1

私は C# の構文にあまり詳しくありませんが、一般的には CurrentToolを、引数としてa を受け入れるメソッドStartDragを持つオブジェクトにすることができます。次に、ビュー上でマウスが押されたときに、 を呼び出します。EndDragDragManagerCurrentTool.StartDrag(m_DragManager)

于 2009-06-03T00:03:49.950 に答える
-2

フラグ列挙型を試してください。各ビットは異なる機能であり、セルごとに機能をより適切に積み重ねることができます。

次に、さまざまな方法でも各ビットをチェックできます。

于 2009-06-03T00:06:36.170 に答える