Советы с иерархией классов для игровых предметов

StackOverflow https://stackoverflow.com/questions/225103

  •  03-07-2019
  •  | 
  •  

Вопрос

Я пишу движок MUD и только что начал работу с объектной моделью игры, которая должна быть расширяемой.

Мне нужна помощь главным образом потому, что то, что я сделал, кажется грязным, но я не могу придумать другое решение, которое работает лучше.

У меня есть класс с именем MudObject и другой класс с именем Container. Контейнер может содержать несколько MudObjects, но сам по себе является Item, однако .containedBy нужно знать, что они содержатся в .

Так они выглядят примерно так:

public abstract class MudObject
{
    Container containedBy;
}

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

(обратите внимание, это всего лишь пример, и некоторые классификаторы и модификаторы доступа, свойства и тому подобное пропущены)

Теперь только это само по себе кажется грязным, но давайте добавим что-то еще к миксу:

<=> - это <=>, от которого будут унаследованы все визуальные предметы (например, оружие), однако некоторые из них также должны быть контейнерами (например, сундуки). Но в C # нет такого, как множественное наследование, так что все сводится к интерфейсам, лучший выбор - сделать контейнер интерфейсом (насколько я вижу). Однако была причина, по которой я не хотел, чтобы это было, в этом случае добавление <=> к контейнеру приведет к тому, что контейнер обновит значение <=> s <=>.

Есть идеи, которые могли бы сделать эту работу, или я попал в ловушку усложнения вещей?
Если так, что еще вы могли бы предложить?

Это было полезно?

Решение

То, что вы просите, является разумным и представляет собой шаблон составного дизайна

Другие советы

Я думаю, что вы слишком усложняете. Если MudObjects может содержать другие MudObjects, то вам нужен один базовый класс:

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

Это похоже на работу WinForms и ASP.NET. Многие контейнерные элементы управления являются элементами управления и могут содержать коллекцию элементов управления.

То, что вы хотите, вполне разумно: оно ничем не отличается от элементов управления Windows, которые сами по себе могут быть контейнером других элементов управления.

Что вам нужно сделать, так это создать собственную реализацию List<MudObject>:

public class MudObjectList : List<MudObject>

которая реализует, помимо прочего, функциональность добавления:

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

Примечание. Этот метод, вместо переопределения, скрывает старую функцию добавления

Таким образом, вы немедленно заполняете атрибут ContainedBy при добавлении. Конечно, подразумевается, что ваш ContainedBy может быть null, что означает, что это объект верхнего уровня.

Наконец, я не думаю, что есть необходимость создавать отдельные классы MudObject и Container, так как контейнер является неотъемлемой частью <=> (ff использует автоматические свойства C # 3.0):

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

Почему бы не сделать все контейнеры MudObjects? ... или, по крайней мере, иметь возможность содержать другие объекты с точки зрения кода вашего класса. Например.

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

Затем вы можете установить какой-либо флаг на самом объекте, чтобы определить, способны ли игроки на самом деле помещать предметы в объект или из него, а не использовать тип объекта, чтобы выяснить это.

На самом деле плохая идея, чтобы что-то было одновременно элементом и контейнером. Это нарушает ряд обязательных сценариев, которые делают предположения о IList; поэтому для сундука у меня может возникнуть соблазн иметь свойство Items для сундука, являющегося коллекцией, но пусть сундук просто будет сундуком.

Тем не менее, на вопрос, который задали ...

Я бы соблазнился сделать MudObject интерфейсом ... таким образом, вы можете использовать что-то вроде следующего, которое дает вам универсальный контейнер для любых конкретных объектов вместе с автоматическим родительским определением:

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

Переходя к идее композиции поверх наследования, ответ, который, кажется, исчез.

Возможно, я мог бы сделать что-то более подобное

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

В дополнение к ответу Марка напишите пару классов, поддерживающих двунаправленные отношения между родителями и детьми.

scroll top