2008-10-22 6 views
2

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

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

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

Так они выглядят что-то как это:

public abstract class MudObject 
{ 
    Container containedBy; 
} 

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

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

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

Item является MudObject, что все визуальные элементы (например, оружие) будут унаследованы от, однако некоторые из них должны быть контейнеры тоже (например, сундуки). Но theres нет, например, множественное наследование в C#. Таким образом, это сводится к интерфейсам, лучшим выбором было бы сделать контейнер интерфейсом (насколько я могу видеть). Однако была причина, по которой я не хотел, чтобы это было, что добавление MudObject в контейнер приведет к обновлению значения MudObject s .containedBy.

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

+0

Обратите внимание на мою точку привязки; вместо того, чтобы сундук * быть * контейнером, подумайте о том, чтобы позволить сундуку иметь свойство (такое как предметы), которое является контейнером ... – 2008-10-22 10:20:36

+0

См. мой собственный ответ :) – Sekhat 2008-10-22 10:39:18

ответ

1

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

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

public class MudObjectList : List<MudObject> 

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

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

Примечание: этот метод тени, вместо переопределений, старая функция добавления

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

Наконец, я не думаю, что есть необходимость сделать отдельные MudObject и Container классов, так как будучи контейнером выглядит присущей к MudObject (ТФ использует C# 3.0 автоматических свойств):

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

Список не предоставляет виртуальное объявление, которое можно переопределить – 2008-10-22 10:16:38

0

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

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

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

0

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

Однако, на вопрос, как просили ...

Я был бы соблазн сделать 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; 
    } 
} 
4

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

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

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

0

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

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

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

Вдоль линий ответ Марка, написать пару классов, которые поддерживают двунаправленную связь родитель-ребенок под капотом.