Domanda

Il problema

Sto cercando di evitare il codice che assomiglia al seguente:

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

Ho pensato di poterlo fare attraverso il sovraccarico del metodo, ma raccoglie sempre il tipo meno derivato, presumo che ciò sia dovuto al fatto che il sovraccarico è determinato al momento della compilazione (a differenza della prevalenza), e quindi solo la classe di base può essere assunta nel seguente codice:

Struttura del codice:

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)

Implementazione del codice (classi chiave):

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

Il mio scenario:In sostanza sto creando un gioco e voglio disaccoppiarsi le classi del mondo reale (come un uomo), da classi sullo schermo (un'immagine di una persona). L'oggetto del mondo reale non dovrebbe avere alcuna conoscenza della sua rappresentazione sullo schermo, la rappresentazione dovrà essere consapevole dell'oggetto reale (per sapere quanti anni ha l'uomo e quindi quante rughe sono disegnate). Voglio avere il fallback in cui se un RealObject è di tipo sconosciuto, mostra ancora qualcosa (come una grande croce rossa).

Si prega di notare che questo codice non è quello che sto usando, è una versione semplificata per mantenere chiara la domanda. Potrei aver bisogno di aggiungere dettagli in seguito, se applicabile, spero che la soluzione a questo codice funzioni anche nell'applicazione.

Qual è il modo più elegante per risolverlo? - Senza il RealObject stesso in possesso di informazioni su come dovrebbe essere rappresentato. Il gioco XNA è una prova del concetto che è molto pesante e, se si rivela fattibile, verrà cambiato da 2D a 3D (probabilmente supportando entrambi per i computer di fascia bassa).

È stato utile?

Soluzione

Usa una fabbrica:

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

Assegna i metodi di fabbrica:

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

E usalo:

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

Per poter restituire immagini diverse per gli uomini puoi usare una condizione:

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

Per chiarezza puoi aggiungere tutti i metodi più complessi a una classe:

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

    }
}

E assegnare il metodo

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

Altri suggerimenti

Se stai usando .NET 4, prova quanto segue:

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

Trasmettendo in dinamico, il tipo effettivo verrà determinato in fase di esecuzione, il che dovrebbe quindi far sì che il sovraccarico corretto venga chiamato.

Potresti creare classi di facciata che accettano il tuo oggetto del mondo reale come argomento costruttore (cioè Manfacade, Womanfacade, ecc.)

Credo che il motivo per cui la classe meno derivata viene chiamata è perché stai facendo il lavoro in una classe esterna. Se si crea il metodo getImage () un membro virtuale della classe RealObject, la versione più derivata dovrebbe essere chiamata. Nota che puoi avere getImage () delegato a ArtManager se lo desideri. Ma la soluzione di @Seairth realizza la stessa cosa e probabilmente sarebbe meno invadente.

Si potrebbe sostenere che mettere getImage () nella classe RealObject viola la responsabilità singola ... Penso che dipenderebbe da come appare il resto della classe. Ma mi sembra che RealWorld.Render non dovrebbe essere responsabile dell'ottenimento delle immagini per ogni REALOBJECT. E così com'è, dovresti toccare ArtManager ogni volta che aggiungi una sottoclasse di RealObject, che viola aperto/chiuso.

Se l'Hiearchy del RealWorld è stabile che potresti usare il Visitor modello.

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; }
    }   
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top