6

問題

次のようなコードを避けようとしています。

If(object Is Man)
  Return Image("Man")
ElseIf(object Is Woman)
  Return Image("Woman")
Else
  Return Image("Unknown Object")

メソッドのオーバーロードでこれを実行できると思いましたが、常に最小の派生型を選択します。これは、オーバーロードがコンパイル時に決定されるため(オーバーライドとは異なり)、したがって、次のコードでは基本クラスのみを想定できるためです。

コード構造:

NS:Real
   RealWorld (Contains a collection of all the RealObjects)
   RealObject
     Person
       Man
       Woman
NS:Virtual
   VirtualWorld (Holds a reference to the RealWorld, and is responsible for rendering)
   Image (The actual representation of the RealWorldObject, could also be a mesh..)
   ArtManager (Decides how an object is to be represented)

コード実装(キークラス):

class VirtualWorld
{
    private RealWorld _world;

    public VirtualWorld(RealWorld world)
    {
        _world = world;
    }

    public void Render()
    {
        foreach (RealObject o in _world.Objects)
        {
            Image img = ArtManager.GetImageForObject(o);
            img.Render();
        }
    }
}

static class ArtManager
{
    public static Image GetImageForObject(RealObject obj)// This is always used
    {
        Image img = new Image("Unknown object");
        return img;
    }

    public static Image GetImageForObject(Man man)
    {
        if(man.Age < 18)
            return new Image("Image of Boy");
        else
            return new Image("Image of Man");
    }

    public static Image GetImageForObject(Woman woman)
    {
        if (woman.Age < 70)
            return new Image("Image of Woman");
        else
            return new Image("Image of Granny");
    }
}

私のシナリオ: 基本的に私はゲームを作成していて、実際のクラス(男性など)を画面上のクラス(人の画像)から切り離したいと考えています。実世界のオブジェクトは、画面上の表現を認識していない必要があります。表現は、実際のオブジェクトを認識している必要があります(男性の年齢、つまり描画されるしわの数を知るため)。RealObjectのタイプが不明な場合でも、何か(大きな赤い十字など)が表示されるフォールバックが必要です。

このコードは私が使用しているものではないことに注意してください。質問を明確にするために簡略化されたバージョンです。該当する場合は、後で詳細を追加する必要があるかもしれません。このコードのソリューションがアプリケーションでも機能することを期待しています。

これを解決するための最もエレガントな方法は何ですか?-RealObject自体が、それをどのように表現するかに関する情報を保持していません。XNAゲームは、AIが非常に重い概念実証であり、実行可能であることが証明された場合は、2Dから3Dに変更されます(おそらく、ローエンドコンピューターの両方をサポートします)。

4

5 に答える 5

4

工場を使用する:

public class ImageFactory
{
    Dictionary<Type, Func<IPerson, Image>> _creators;

    void Assign<TPerson>(Func<IPerson, Image> imageCreator) where T : IPerson
    {
       _creators.Add(typeof(TPerson), imageCreator);
    }

   void Create(Person person)
   {
       Func<IPerson, Image> creator;
       if (!_creators.TryGetValue(person.GetType(), out creator))
          return null;

       return creator(person);
   }
}

ファクトリメソッドの割り当て:

imageFactory.Assign<Man>(person => new Image("Man");
imageFactory.Assign<Woman>(person => new Image("Big bad mommy");
imageFactory.Assign<Mice>(person => new Image("Tiny little mouse");

そしてそれを使用してください:

var imageOfSomeone = imageFactory.Create(man);
var imageOfSomeone2 = imageFactory.Create(woman);
var imageOfSomeone3 = imageFactory.Create(mice);

男性用に異なる画像を返すことができるようにするには、条件を使用できます。

factory.Assign<Man>(person => person.Age > 10 ? new Image("Man") : new Image("Boy"));

わかりやすくするために、より複雑なメソッドをすべてクラスに追加できます。

public static class PersonImageBuilders
{
    public static Image CreateMen(IPerson person)
    {
        if (person.Age > 60)
            return new Image("Old and gready!");
        else
            return new Image("Young and foolish!");

    }
}

そして、メソッドを割り当てます

imageFactory.Assign<Man>(PersonImageBuilders.CreateMen);
于 2011-05-03T14:23:06.570 に答える
1

.NET 4を使用している場合は、次のことを試してください。

Image img = ArtManager.GetImageForObject((dynamic)o);

動的にキャストすることにより、実際のタイプが実行時に決定され、正しいオーバーロードが呼び出されるようになります。

于 2011-05-03T14:23:47.947 に答える
0

実世界のオブジェクトをコンストラクター引数として受け入れるFacadeクラスを作成できます(つまり、ManFacade、WomanFacadeなど)。

于 2011-05-03T14:12:13.007 に答える
0

最小派生クラスが呼び出される理由は、外部クラスで作業を行っているためだと思います。GetImage()メソッドをRealObjectクラスの仮想メンバーにすると、最も派生したバージョンが呼び出されます。必要に応じて、GetImage()をArtManagerに委任できることに注意してください。しかし、@ searthのソリューションは同じことを達成し、おそらく邪魔にならないでしょう。

GetImage()をRealObjectクラスに入れると、単一責任に違反すると主張する人もいるかもしれません...それはクラスの残りの部分がどのように見えるかに依存すると思います。しかし、RealWorld.Renderは、各RealObjectの画像を取得する責任を負わないように思われます。そして、そのままでは、RealObjectのサブクラスを追加するたびにArtManagerにタッチする必要があります。これは、Open/Closedに違反します。

于 2011-05-03T15:26:24.690 に答える
-1

の階層が安定している場合は、パターンRealWorldを使用できます。Visitor

public abstract class RealObject
{
    public abstract void Accept(RealObjectVisitor visitor);
}

public class Man : RealObject
{
    public override void Accept(RealObjectVisitor visitor)
    {
        visitor.VisitMan(this);
    }
}

public class Woman : RealObject
{
    public override void Accept(RealObjectVisitor visitor)
    {
        visitor.VisitWoman(this);
    }
}

public abstract class RealObjectVistor
{
    public abstract void VisitMan(Man man);
    public abstract void VisitWoman(Woman woman);        
}


public class VirtualObjectFactory
{
    public VirtualObject Create(RealObject realObject)
    {
        Visitor visitor = new Visitor();
        realObject.Accept(visitor);
        return visitor.VirtualObject;
    }  

    private class Visitor : RealObjectVistor
    {  
        public override void VisitMan(Man man)
        {
            VirtualObject = new ManVirtualObject(man);
        }

        public override void VisitWoman(Woman woman)
        {
            VirtualObject = new WomanVirtualObject(woman);
        }

        public VirtualObject VirtualObject { get; private set; }
    }   
}
于 2011-05-03T14:25:44.423 に答える