Question

J'écris un moteur MUD et je viens de commencer le modèle d'objet du jeu, qui doit être extensible.

J'ai besoin d'aide principalement parce que ce que j'ai fait est désordonné, mais je ne peux pas penser à une autre solution qui fonctionne mieux.

J'ai une classe appelée MudObject, et une autre classe appelée Container, un conteneur peut contenir plusieurs MudObjects, mais est un Item lui-même, mais .containedBy a besoin de savoir en quoi ils sont contenus .

Ils ressemblent donc à quelque chose comme ceci:

public abstract class MudObject
{
    Container containedBy;
}

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

(veuillez noter que ce ne sont que des exemples et que certains qualificateurs et modificateurs d'accès, propriétés et autres ne sont pas renseignés)

Maintenant, cela semble compliqué en soi, mais ajoutons quelque chose d'autre au mélange:

<=> est un <=> élément dont tous les éléments visuels (tels que les armes) seront hérités, mais certains d'entre eux doivent également être des conteneurs (comme des coffres). Mais il n’existe pas d’héritage multiple en c #, c’est donc aux interfaces que le mieux serait de faire du conteneur une interface (pour autant que je puisse voir). Cependant, il y avait une raison pour laquelle je ne le voulais pas, cela étant que l'ajout d'un <=> à un conteneur amènera le conteneur à mettre à jour la valeur <=> s <=>.

Des idées pour que cela fonctionne ou suis-je tombé dans le piège de rendre les choses trop compliquées?
Si oui, que pourriez-vous suggérer d'autre?

Était-ce utile?

La solution

Ce que vous demandez est raisonnable et correspond au modèle de conception composite

Autres conseils

Je pense que vous compliquez trop. Si MudObjects peut contenir d’autres MudObjects, la classe de base unique dont vous avez besoin devrait suivre les lignes suivantes:

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

Ceci est similaire au fonctionnement de WinForms et ASP.NET. De nombreux contrôles de conteneur sont à la fois des contrôles et peuvent contenir une collection de sous-contrôles.

Ce que vous voulez est tout à fait raisonnable: ce n'est pas différent des contrôles de formulaire Windows, qui peuvent eux-mêmes être un conteneur d'autres contrôles.

Ce que vous devez faire est de créer votre propre implémentation de List<MudObject>:

public class MudObjectList : List<MudObject>

qui implémente, entre autres, la fonctionnalité d'ajout:

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

Remarque: cette méthode ombre, au lieu de remplacer, l'ancienne fonctionnalité d'ajout

.

De cette manière, vous complétez immédiatement l'attribut ContainedBy lors de l'ajout. Bien entendu, cela signifie que votre contenu doit être null, ce qui signifie qu'il s'agit de l'objet de niveau supérieur.

Enfin, je ne pense pas qu'il soit nécessaire de créer des classes MudObject et Container séparées, car le fait d'être un conteneur est intrinsèque à un <=> (le ff utilise les propriétés automatiques C # 3.0):

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

Pourquoi ne pas créer tous les conteneurs MudObjects? ... ou du moins, avoir la capacité de contenir d'autres objets, en termes de code de classe. Ex.

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

Vous pouvez ensuite définir une sorte d’indicateur sur l’objet lui-même pour déterminer si les joueurs sont capables d’ajouter ou non des objets dans l’objet, plutôt que d’utiliser le type de l’objet pour le déterminer.

En fait, c’est une mauvaise idée que quelque chose soit à la fois un élément et un conteneur. Cela rompt un certain nombre de scénarios contraignants qui font des hypothèses sur IList; Donc, pour un coffre, je pourrais être tenté d'avoir une propriété Items sur le coffre qui est la collection, mais laissez le coffre juste un coffre.

Cependant, pour la question telle que posée ...

Je serais tenté de transformer l'interface MudObject en interface ... de cette façon, vous pouvez utiliser le suivant, qui vous donne un conteneur générique d'objets concrets, ainsi qu'un mode parental automatique:

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

Aborder l’idée de composition sur héritage réponse qui semble avoir disparu.

Je pourrais peut-être faire quelque chose de plus semblable à ceci

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

Dans le sens de la réponse de Marc, écrivez quelques classes qui maintiennent une relation parent-enfant bidirectionnelle sous le capot.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top