【问题标题】:Advice with Class Hierarchy for Game Items游戏项目的类层次结构建议
【发布时间】:2008-10-22 10:06:27
【问题描述】:

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

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

我有一个叫MudObject的类,还有一个叫Container的类,一个容器可以包含多个MudObjects,但它本身是一个MudObject,但是MudObjects需要知道它们包含在什么.

所以它们看起来像这样:

public abstract class MudObject
{
    Container containedBy;
}

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

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

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

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

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

【问题讨论】:

  • 请注意我关于绑定的观点;与其让箱子成为一个容器,不如考虑让箱子有一个属性(例如Items),它就是容器......

标签: c# oop hierarchy mud


【解决方案1】:

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

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

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

【讨论】:

  • 但不是所有的 MudObjects 都可以包含其他 MudObjects
  • 这种设计使维护变得困难,因为您需要手动维护父子关系。
  • @Killersponge,如果是这种情况,您可以将包含列表的访问权限设置为属性,并添加一个允许对象具有子对象的属性。
【解决方案2】:

你的要求是合理的,是Composite Design Pattern

【讨论】:

    【解决方案3】:

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

    您需要做的是创建自己的List&lt;MudObject&gt; 实现:

    public class MudObjectList : List<MudObject>
    

    其中实现了添加功能:

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

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

    通过这种方式,您可以在添加时立即填充 ContainedBy 属性。当然暗示你的 ContainedBy 可以是null,也就是说它是顶级对象。

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

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

    【讨论】:

    • List 不公开您可以覆盖的虚拟添加
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

      实际上,将某物同时作为项目和容器是一个坏主意。这打破了许多对 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;
          }
      }
      

      【讨论】:

        【解决方案6】:

        采用组合而不是继承的想法似乎已经消失了。

        也许我可以做更多这样的事情

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-05
            • 2017-09-25
            相关资源
            最近更新 更多