2

我正在编写一个 MUD 引擎,我刚刚开始研究需要可扩展的游戏对象模型。

我需要帮助主要是因为我所做的事情感觉很混乱,但我想不出另一种更好的解决方案。

我有一个名为 的类MudObject,另一个名为的类Container,一个容器可以包含多个MudObjects,但它MudObject本身就是一个,但是MudObject需要知道它们包含在什么中。

所以它们看起来像这样:

public abstract class MudObject
{
    Container containedBy;
}

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

(请注意,这些只是示例,一些限定符和访问修饰符、属性等被遗漏了)

现在这本身看起来很混乱,但让我们添加一些其他的东西:

Item是一个MudObject所有视觉物品(例如武器)都将从中继承的,但是其中一些也需要是容器(例如箱子)。但是在c#中没有多重继承之类的,所以归结为接口,最好的选择是使容器成为接口(据我所知)但是我不希望它是有原因的,那就是向容器中添加一个MudObject将导致容器更新MudObjects.containedBy值。

有什么想法可以完成这项工作,还是我陷入了让事情变得过于复杂的陷阱?
如果是这样,你还有什么建议?

4

7 回答 7

4

我认为你过于复杂了。如果 MudObjects 可以包含其他 MudObjects,则您需要的单个基类应遵循以下原则:

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

这类似于 WinForms 和 ASP.NET 的工作方式。许多容器控件既是控件,又可以包含子控件的集合。

于 2008-10-22T10:17:08.357 回答
1

你想要的很合理:它与 Windows 窗体控件没有什么不同,它本身可以是其他控件的容器。

您需要做的是创建自己的实现List<MudObject>

public class MudObjectList : List<MudObject>

除其他外,它实现了添加功能:

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

注意:此方法隐藏而不是覆盖旧的 Add 功能

通过这种方式,您可以在添加时立即填充 ContainedBy 属性。当然,这意味着您的 ContainedBy 可以是null,这意味着它是顶级对象。

最后,我认为不需要单独创建MudObjectContainer类,因为作为容器看起来是 a 所固有的MudObject(ff 使用 C# 3.0 自动属性):

public abstract class MudObject
{
    MudObject ContainedBy { get; set; }
    MudObjectList Contains { get; set; }
}
于 2008-10-22T10:14:19.673 回答
1

您要求的是合理的,并且是复合设计模式

于 2008-10-22T19:39:00.250 回答
0

为什么不制作所有 MudObjects 容器?...或者至少,就您的类代码而言,能够包含其他对象。例如

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

然后,您可以在对象本身上设置某种标志,以识别玩家是否能够实际将东西放入或取出对象,而不是使用对象的类型来确定。

于 2008-10-22T10:15:17.643 回答
0

实际上,将某物既作为项目作为容器是一个坏主意。这打破了许多对 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;
    }
}
于 2008-10-22T10:15:24.897 回答
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; }
}
于 2008-10-22T10:37:43.463 回答
0

按照 Marc 的回答,编写几个在后台保持双向父子关系的类。

于 2008-10-22T11:05:54.483 回答