我正在尝试实施一个高性能的游戏库存系统。我有这个抽象基类来存储库存中不同类型的项目,例如,硬币,手电筒,刀等。
public abstract class ObtainableItem
{
public string Name { get; private set; }
public ObtainableItem(string name)
{
Name = name;
}
}
例如,我有一个打开门的DoorKey。 DoorKey有一个属性KeyCode,用于打开一扇门。
public class DoorKey : ObtainableItem
{
public int KeyCode { get; private set; }
public DoorKey() : base("key")
{
KeyCode = 1234;
}
}
所有ObtainableItem都存储在Inventory
中public class Inventory
{
const int slotCount = 2;
ObtainableItem[] slots = new ObtainableItem[slotCount];
public Inventory()
{
slots[0] = new DoorKey();
}
}
现在假设用户从门上的库存中拖动DoorKey并触发Open方法
public class Door
{
public void Open(ObtainableItem key)
{
if (key is DoorKey)
{
DoorKey doorKey = (DoorKey)key;
if (doorKey.KeyCode == 1234)
{
// Open door
}
}
else
{
// "can't use this item on a door"
}
}
}
如何避免从ObtainableItem转换为DoorKey?我已经读过,使用强制转换是不好的做法,它指的是糟糕的代码oop设计。理想情况下,Door类应该如下所示。我的库存系统有什么样的模式吗?
public class Door
{
public void Open(DoorKey key)
{
if (key.KeyCode == 1234)
{
// Open door
}
}
}
答案 0 :(得分:1)
为了易于实现和可读性,总会有例外情况。您所描述的内容很常见,如果不是典型的话。
另一种方法是在调用Door.Open
的类中使用“控制”逻辑。这可以通过反思轻松实现:
public abstract class ObtainableItem
{
public string Name { get; private set; }
public ObtainableItem(string name)
{
Name = name;
}
}
public abstract class WorldItem
{
}
public interface IActsOn<in TWorldItem>
where TWorldItem : WorldItem
{
void ApplyTo(TWorldItem worldItem);
}
public class World
{
// If profiling shows that this is a performance issue, a cache keyed by tWorldItem, tInvItem
// should fix it. No expiry or invalidation should be needed.
private Action<ObtainableItem, WorldItem> GetApplyTo(Type tWorldItem, Type tInvItem)
{
var tActOn = typeof(IActsOn<>).MakeGenericType(tWorldItem);
if (!tActOn.IsAssignableFrom(tInvItem))
{
return null;
}
var methodInfo = tActOn.GetMethod(nameof(IActsOn<WorldItem>.ApplyTo));
return new Action<ObtainableItem, WorldItem>((invItem, worldItem) =>
{
methodInfo.Invoke(invItem, new object[] { worldItem });
});
}
public bool IsDropTarget(WorldItem worldItem, ObtainableItem item)
=> GetApplyTo(worldItem.GetType(), item.GetType()) != null;
public void ActOn(WorldItem worldItem, ObtainableItem item)
{
var actOn = GetApplyTo(worldItem.GetType(), item.GetType());
if (actOn == null)
{
throw new InvalidOperationException();
}
actOn(item, worldItem);
}
}
虽然这会使World
的实现稍微复杂化,但它简化了各种对象的实现:
class Door : WorldItem
{
public void Unlock(string bitting)
{
if (bitting == "1234")
{
Console.WriteLine("Door Opened");
}
else
{
Console.WriteLine("Door could not unlock");
}
}
}
class DoorKey : ObtainableItem, IActsOn<Door>
{
private readonly string Bitting;
public DoorKey(string bitting)
: base("Key")
{
this.Bitting = bitting;
}
public void ApplyTo(Door worldItem)
{
worldItem.Unlock(this.Bitting);
}
}
class RubberChicken : ObtainableItem
{
public RubberChicken()
: base("Rubber chicken")
{
}
}
使用示例:
class Program
{
static void Main(string[] args)
{
var key1 = new DoorKey("1234");
var key2 = new DoorKey("4321");
var rubberChicken = new RubberChicken();
var door = new Door();
var world = new World();
Debug.Assert(!world.IsDropTarget(door, rubberChicken));
Debug.Assert(world.IsDropTarget(door, key1));
world.ActOn(door, key2);
world.ActOn(door, key1);
Console.ReadLine();
}
}