質問
MUDエンジンを書いていますが、ゲームオブジェクトモデルを始めたばかりです。これは拡張可能である必要があります。
主に私がやったことは面倒だと思うので助けが必要ですが、私はより良い動作をする別の解決策を考えることができません。
MudObject
というクラスと、Container
という別のクラスがあります。コンテナには複数のMudObjects
を含めることができますが、Item
自体ですが、.containedBy
sには何が含まれているかを知る必要があります。
したがって、これらは次のようになります。
public abstract class MudObject
{
Container containedBy;
}
public abstract class Container : MudObject
{
List<MudObject> Contains;
}
(これらは単なる例であり、いくつかの修飾子とアクセス修飾子、プロパティなどが欠落していることに注意してください)
今ではこれ自体は面倒に見えますが、ミックスに何か他のものを追加できます:
<=>は<=>で、すべての視覚アイテム(武器など)が継承されますが、これらの一部はコンテナー(チェストなど)である必要もあります。しかし、c#には多重継承などはありません。したがって、インターフェースになります。コンテナをインターフェースにすることが最良の選択です(私が見る限り)。しかし、それを望まない理由がありました。つまり、コンテナに<=>を追加すると、コンテナは<=> s <=>値を更新します。
この作品を作るアイデアはありますか、それとも物事を複雑にしすぎるというtrapに陥っていますか?
もしそうなら、他に何を提案できますか?
解決
求めているのは合理的であり、複合デザインパターン
他のヒント
あなたは複雑すぎると思います。 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);
}
注:このメソッドは、古いAdd機能をオーバーライドする代わりにシャドウします
この方法では、追加時にすぐに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; }
}
マークの答えに沿って、内部で双方向の親子関係を維持するいくつかのクラスを記述します。