我经常遇到的一个基本问题,但曾经找到一个干净的解决方案,就是你想要为公共基类或接口的不同对象之间的交互编写行为代码。为了使其更具体,我将举一个例子;
Bob 一直在编写一款支持“酷地理效应”的策略游戏。这些都是简单的限制,例如如果部队在水中行走,他们会减慢 25%。如果他们在草地上行走,他们会减慢 5%,如果他们在人行道上行走,他们会减慢 0%。
现在,管理层告诉鲍勃他们需要新的部队。会有吉普车、船和气垫船。此外,他们希望吉普车在驶入水中时受到伤害,而气垫船将忽略所有三种地形类型。也有传言说他们可能会添加另一种地形类型,其功能比减缓单位减速和受到伤害还要多。
一个非常粗略的伪代码示例如下:
public interface ITerrain
{
void AffectUnit(IUnit unit);
}
public class Water : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.75f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.70f;
unit.Health -= 5.0f;
}
if (unit is Boat)
{
// Don't affect it anyhow
}
/*
* List grows larger each day...
*/
}
}
public class Grass : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.95f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.85f;
}
if (unit is Boat)
{
unit.SpeedMultiplier = 0.0f;
unit.Health = 0.0f;
Boat boat = unit as Boat;
boat.DamagePropeller();
// Perhaps throw in an explosion aswell?
}
/*
* List grows larger each day...
*/
}
}
如您所见,如果 Bob 从一开始就有可靠的设计文档,情况会更好。随着单位数量和地形类型的增加,代码的复杂性也在增加。Bob 不仅需要担心确定哪些成员可能需要添加到单元界面,而且他还必须重复大量代码。新的地形类型很可能需要从基本 IUnit 接口获得的附加信息。
每次我们在游戏中添加另一个单位时,都必须更新每个地形以处理新单位。显然,这会导致大量重复,更不用说丑陋的运行时检查,它决定了正在处理的单元的类型。在此示例中,我选择了对特定子类型的调用,但这些调用是必需的。一个例子是,当一艘船撞到陆地时,它的螺旋桨应该被损坏。并非所有单位都有螺旋桨。
我不确定这种问题叫什么,但它是一个多对多的依赖关系,我很难解耦。我不希望 ITerrain 上的每个 IUnit 子类都有 100 个重载,因为我想通过耦合来干净利落。
任何关于这个问题的观点都备受追捧。也许我正在考虑一起脱离轨道?