Pregunta

El problema

Estoy tratando de evitar el código que se parece al siguiente:

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

Pensé que podría hacer esto a través de la sobrecarga de métodos, pero siempre elige el tipo menos derivado, supongo que esto se debe a que la sobrecarga se determina en el tiempo de compilación (a diferencia de la anulación) y, por lo tanto, solo la clase base se puede suponer en el siguiente código:

Estructura del código:

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)

Implementación de código (clases clave):

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

Mi escenario:Esencialmente, estoy creando un juego y quiero desacoplar las clases del mundo real (como un hombre), de las clases en pantalla (una imagen de una persona). El objeto del mundo real no debe tener conocimiento de su representación en pantalla, la representación deberá ser consciente del objeto real (saber cuántos años tiene el hombre y, por lo tanto, cuántas arrugas se dibujan). Quiero tener el respaldo donde si un RealObject es de un tipo desconocido, todavía muestra algo (como una gran Cruz Roja).

Tenga en cuenta que este código no es lo que estoy usando, es una versión simplificada para mantener la pregunta clara. Es posible que necesite agregar detalles más adelante si corresponde, espero que la solución a este código también funcione en la aplicación.

¿Cuál es la forma más elegante de resolver esto? - Sin el RealObject en sí sosteniendo información sobre cómo debe representarse. El juego XNA es una prueba de concepto que es muy pesada, y si resulta factible, se cambiará de 2D a 3D (probablemente admite ambas para las computadoras de extremo inferior).

¿Fue útil?

Solución

Use una fábrica:

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

Asignar métodos de fábrica:

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

Y úsalo:

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

Para poder devolver diferentes imágenes para hombres, puede usar una condición:

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

Para mayor claridad, puede agregar todos los métodos más complejos a una clase:

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

    }
}

Y asignar el método

imageFactory.Assign<Man>(PersonImageBuilders.CreateMen);

Otros consejos

Si está utilizando .NET 4, intente lo siguiente:

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

Al lanzar a Dynamic, el tipo real se determinará en tiempo de ejecución, lo que debería hacer que se llame a la sobrecarga correcta.

Puede crear clases de fachada que acepten su objeto del mundo real como argumento de constructor (es decir, Manfacade, WomanFacade, etc.)

Creo que la razón por la que se llama a la clase menos derivada es porque estás haciendo el trabajo en una clase externa. Si hace el método getImage () un miembro virtual de la clase RealObject, entonces se debe llamar a la versión más derivada. Tenga en cuenta que puede tener GetImage () delegado al ArtManager si lo desea. Pero la solución de @Seeirth logra lo mismo y probablemente sería menos intrusivo.

Se podría argumentar que poner GetImage () en la clase RealObject viola la responsabilidad única ... Creo que eso dependería de cómo se ve el resto de la clase. Pero me parece que RealWorld.Render no debería ser responsable de obtener las imágenes para cada RealObject. Y tal como está, tendrías que tocar ArtManager cada vez que agrega una subclase de RealObject, que viola abierta/cerrada.

Si el HiOchy del RealWorld es estable, podrías usar el Visitor patrón.

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; }
    }   
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top