Pregunta

Estoy escribiendo un motor MUD y acabo de comenzar con el modelo de objetos del juego, que debe ser extensible.

Necesito ayuda principalmente porque lo que he hecho parece complicado, pero no se me ocurre otra solución que funcione mejor.

tengo una clase llamada MudObject, y otra clase llamada Container, Un contenedor puede contener múltiples MudObjects, pero es un MudObject sí mismo, sin embargo MudObjectNecesitamos saber en qué están contenidos.

Entonces se ven así:

public abstract class MudObject
{
    Container containedBy;
}

public abstract class Container : MudObject
{
    List<MudObject> Contains;
}

(tenga en cuenta que estos son solo ejemplos y que algunos calificadores y modificadores de acceso, propiedades y demás no se incluyen)

Ahora bien, esto en sí mismo parece complicado, pero agreguemos algo más a la mezcla:

Item es un MudObject del que se heredarán todos los elementos visuales (como armas), sin embargo, algunos de ellos también deben ser contenedores (como cofres).Pero no existe la herencia múltiple en C#. Por lo tanto, todo se reduce a las interfaces, la mejor opción sería hacer que el contenedor sea una interfaz (hasta donde puedo ver). Sin embargo, había una razón por la que no quería que lo fuera. siendo eso agregar un MudObject a un contenedor hará que el contenedor actualice el MudObjects .containedBy valor.

¿Alguna idea que pueda hacer que esto funcione, o estoy cayendo en la trampa de complicar demasiado las cosas?
Si es así, ¿qué más podría sugerir?

¿Fue útil?

Solución

Lo que está pidiendo es razonable, y es el Patrón de diseño compuesto

Otros consejos

Creo que te estás complicando demasiado. Si MudObjects puede contener otros MudObjects, la clase base única que necesita debe estar en esta línea:

public abstract class MudObject
{    
    MudObject containedBy; //technically Parent
    List<MudObject> Contains; //children
}

Esto es similar a la forma en que funciona WinForms y ASP.NET. Muchos controles de contenedor son ambos controles y pueden contener una colección de subcontroles.

Lo que quieres es bastante razonable:no es diferente de los controles de formulario de Windows, que a su vez pueden ser un contenedor de otros controles.

Lo que sí debe hacer es crear su propia implementación de List<MudObject>:

public class MudObjectList : List<MudObject>

que implementa, entre otras cosas, la funcionalidad de agregar:

public void new Add(MudObject obj)
{
    obj.ContainedBy = this;
    base.Add(obj);
}

Nota:este método oculta, en lugar de anular, la antigua funcionalidad Agregar

De esta manera, completa inmediatamente el atributo ContainedBy al agregarlo.Por supuesto, está implícito que su ContainedBy puede ser null, lo que significa que es el objeto de nivel superior.

Finalmente, no creo que sea necesario separar MudObject y Container clases, ya que ser un contenedor parece intrínseco a un MudObject (el ff usa propiedades automáticas de C# 3.0):

public abstract class MudObject
{
    MudObject ContainedBy { get; set; }
    MudObjectList Contains { get; set; }
}

¿Por qué no hacer todos los contenedores de MudObjects? ... o al menos, tienen la capacidad de contener otros objetos, en términos de su código de clase. Por ejemplo,

public abstract class MudObject
{
    MudObject containedBy;
    List<MudObject> contains;
}

A continuación, puede establecer algún tipo de indicador en el objeto en sí para identificar si los jugadores son capaces de poner cosas dentro o fuera del objeto, en lugar de utilizar el tipo de objeto para resolverlo.

En realidad, es una mala idea que algo sea tanto un elemento como un contenedor. Esto rompe una serie de escenarios vinculantes que hacen suposiciones sobre IList; así que para un cofre, podría tener la tentación de tener una propiedad Items en el cofre que es la colección, pero dejar que el cofre sea solo un cofre.

Sin embargo, para la pregunta formulada ...

Me sentiría tentado a hacer de MudObject la interfaz ... de esa manera, puede usar algo como lo siguiente, que le brinda un contenedor genérico de cualquier objeto concreto, junto con la función de crianza automática:

public interface IMudObject
{
    IMudObject Container { get; set; }
    /* etc */
}

public class MudContainer<T> : Collection<T>, IMudObject
    where T : IMudObject
{

    public IMudObject Container { get; set; }

    protected override void ClearItems()
    {
        foreach (T item in this)
        {
            RemoveAsContainer(item);
        }
        base.ClearItems();
    }

    protected override void InsertItem(int index, T item)
    {
        SetAsContainer(item);
        base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {
        RemoveAsContainer(this[index]);
        base.RemoveItem(index);            
    }
    protected override void SetItem(int index, T item)
    {
        RemoveAsContainer(this[index]);
        SetAsContainer(item);
        base.SetItem(index, item);
    }

    void RemoveAsContainer(T item)
    {
        if (item != null && ReferenceEquals(item.Container, this))
        {
            item.Container = null;
        }
    }
    void SetAsContainer(T item)
    {
        if (item.Container != null)
        {
            throw new InvalidOperationException("Already owned!");
        }
        item.Container = this;
    }
}

Ir con la idea de la composición sobre la respuesta de herencia que parece haberse desvanecido.

Quizás podría hacer algo más como esto

public class Container<T> where T : MudObject
{
    List<T> Contains;
    MudObject containerOwner;

    public Container(MudObject owner)
    {
        containerOwner = owner;
    }
    // Other methods to handle parent association
}

public interface IMudContainer<T> where T : MudObject
{
    Container<T> Contains { get; }
}

public class MudObjectThatContainsStuff : IMudContainer
{
    public MudObjectThatContainsStuff()
    {
        Contains = new Container<MudObject>(this);
    }

    public Contains { get; }
}

En la línea de la respuesta de Marc, escriba un par de clases que mantengan una relación bidireccional padre-hijo bajo el capó.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top