使用类层次结构重载 - 大多数派生未使用

发布于 2024-11-04 23:20:03 字数 2051 浏览 6 评论 0原文

问题

我试图避免如下所示的代码:

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 游戏是一个概念验证,人工智能含量很高,如果证明可行,将从 2D 更改为 3D(可能支持低端计算机)。

The problem

I am trying to avoid code that looks like the following:

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

I thought I could do this through method overloading, but it always picks the least derived type, I assume this is because the overloading is determined at compile time (unlike overriding), and therefore only the base class can be assumed in the following code:

Code structure:

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)

Code Implementation (key classes):

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");
    }
}

My scenario:
Essentially I am creating a game, and want to decouple real-world classes (such as a man), from on-screen classes (an image of a person). The real world object should have no knowledge of it's on-screen representation, the representation will need to be aware of the real object (to know how old the man is, and therefore how many wrinkles are drawn). I want to have the fallback where if a RealObject is of an unknown type, it still displays something (like a big red cross).

Please note that this code is not what i'm using, it's a simplified version to keep the question clear. I may need to add details later if applicable, I'm hoping the solution to this code will also work in the application.

What's the most elegant way to solve this? - Without the RealObject itself holding information on how it should be represented. The XNA game is a proof of concept which is very AI heavy, and if it proves doable, will be changed from 2D to 3D (probably supporting both for lower end computers).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

酒儿 2024-11-11 23:20:03

使用工厂:

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);

Use a factory:

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);
   }
}

Assign factory methods:

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");

And use it:

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

To be able to return different images for men you can use a condition:

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

For clarity you can add all more complex methods to a class:

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!");

    }
}

And assign the method

imageFactory.Assign<Man>(PersonImageBuilders.CreateMen);
太傻旳人生 2024-11-11 23:20:03

如果您使用的是 .NET 4,请尝试以下操作:

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

通过强制转换为动态,将在运行时确定实际类型,这将导致调用正确的重载。

If you are using .NET 4, try the following:

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

By casting to dynamic, the actual type will be determined at runtime, which should then cause the correct overload to be called.

泪是无色的血 2024-11-11 23:20:03

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

You could create Facade classes that accept your real-world object as a constructor argument (ie ManFacade, WomanFacade, etc.)

星星的軌跡 2024-11-11 23:20:03

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

有人可能会说,将 GetImage() 放在 RealObject 类中违反了单一职责……我认为这取决于类的其余部分的外观。但在我看来,RealWorld.Render 不应该负责获取每个 RealObject 的图像。事实上,每次添加 RealObject 的子类时都必须触摸 ArtManager,这违反了 Open/Closed。

I believe the reason the least derived class is being called is because you're doing the work in an external class. If you make the GetImage() method a virtual member of the RealObject class then the most-derived version should get called. Note that you can have GetImage() delegate to the ArtManager if you want.
But @seairth's solution accomplishes the same thing and would probably be less intrusive.

One could argue that putting GetImage() in the RealObject class violates Single Responsibility ... I think that would depend on what the rest of the class looks like. But it seems to me that RealWorld.Render shouldn't be responsible for obtaining the images for each RealObject. And as it is, you'd have to touch ArtManager each time you add a subclass of RealObject, which violates Open/Closed.

乖不如嘢 2024-11-11 23:20:03

如果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; }
    }   
}

If the hiearchy of the RealWorld is stable you could use the Visitor pattern.

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; }
    }   
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文