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

您可以创建接受您的真实世界对象作为构造函数参数的外观类(即 ManFacade、WomanFacade 等)

于 2011-05-03T14:12:13.007 回答
0

我相信调用最少派生类的原因是因为您正在外部类中进行工作。如果您使 GetImage() 方法成为 RealObject 类的虚拟成员,那么应该调用最派生的版本。请注意,如果需要,您可以将 GetImage() 委托给 ArtManager。但是@seairth 的解决方案完成了同样的事情,并且可能会减少干扰。

有人可能会争辩说,将 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 回答