Domanda

Sto scrivendo un motore MUD e ho appena iniziato il modello a oggetti di gioco, che deve essere estensibile.

Ho bisogno di aiuto principalmente perché quello che ho fatto sembra disordinato, ma non riesco a pensare a un'altra soluzione che funzioni meglio.

Ho una classe chiamata MudObject e un'altra classe chiamata Container, Un contenitore può contenere più MudObjects, ma è un Item stesso, tuttavia .containedBy è necessario sapere in cosa sono contenuti .

Quindi sembrano simili a questo:

public abstract class MudObject
{
    Container containedBy;
}

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

(si prega di notare che questi sono solo esempi e alcuni qualificatori e modificatori di accesso, proprietà e simili vengono persi)

Ora, questo di per sé sembra disordinato, ma consente di aggiungere qualcos'altro al mix:

<=> è un <=> da cui verranno ereditati tutti gli oggetti visivi (come le armi), tuttavia alcuni di questi devono essere anche contenitori (come forzieri). Ma non esiste un'ereditarietà multipla in c #, quindi si tratta di interfacce, la scelta migliore sarebbe quella di rendere il contenitore un'interfaccia (per quanto posso vedere) Tuttavia c'era una ragione per cui non volevo che fosse, dato che aggiungendo un <=> a un contenitore, il contenitore aggiornerà il valore <=> s <=>.

Qualche idea che possa far funzionare questo, o sto cadendo nella trappola di rendere le cose troppo complicate?
Se sì, cos'altro potresti suggerire?

È stato utile?

Soluzione

Quello che stai chiedendo è ragionevole, ed è il Composite Design Pattern

Altri suggerimenti

Penso che tu stia complicando troppo. Se MudObjects può contenere altri MudObjects, la singola classe base di cui hai bisogno dovrebbe essere lungo queste linee:

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

Questo è simile al modo in cui WinForms e ASP.NET funzionano. Molti controlli contenitore sono entrambi controlli e possono contenere una raccolta di sottocontrolli.

Quello che vuoi è abbastanza ragionevole: non è diverso dai controlli dei moduli di Windows, che possono essere essi stessi un contenitore di altri controlli.

Quello che devi fare è creare la tua implementazione di List<MudObject>:

public class MudObjectList : List<MudObject>

che implementa, tra le altre cose, la funzionalità di aggiunta:

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

Nota: questo metodo ombreggia, anziché sostituire, la vecchia funzionalità Aggiungi

In questo modo si popola immediatamente l'attributo ContainedBy al momento dell'aggiunta. Naturalmente è implicito che ContainedBy può essere null, il che significa che è l'oggetto di livello superiore.

Infine, non credo che sia necessario creare classi MudObject e Container separate, poiché essere un contenitore sembra intrinseco a un <=> (il file ff usa le proprietà automatiche di C # 3.0):

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

Perché non creare tutti i contenitori MudObjects? ... o almeno, hanno la capacità di contenere altri oggetti, in termini di codice di classe. Per es.

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

Puoi quindi impostare una sorta di bandiera sull'oggetto stesso per identificare se i giocatori sono effettivamente in grado di mettere le cose dentro o fuori l'oggetto stesso, piuttosto che usare il tipo di oggetto per capirlo.

In realtà, è una cattiva idea che qualcosa sia sia un elemento che un contenitore. Ciò interrompe una serie di scenari vincolanti che fanno ipotesi su IList; quindi per un forziere, potrei essere tentato di avere una proprietà Items su sul forziere che è la collezione, ma lascia che il forziere sia solo un baule.

Tuttavia, per la domanda come posta ...

Sarei tentato di rendere l'interfaccia MudObject ... in questo modo, puoi usare qualcosa come il seguente, che ti dà un contenitore generico di qualsiasi oggetto concreto, insieme alla genitorialità automatica:

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

Accompagnando l'idea della composizione sulla risposta ereditaria che sembra essere svanita.

Forse potrei fare qualcosa di più simile a questo

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

Sulla falsariga della risposta di Marc, scrivi un paio di lezioni che mantengano una relazione bidirezionale genitore-figlio sotto il cofano.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top