限制中间抽象类的继承



我有一个抽象类,CreatureBehaviour,它提供了一个TakeTurn方法。这样做的目的是决定生物应该做什么,它应该通过回调提供响应。这是因为响应可能需要玩家输入,因此不应阻止其他进程。

public abstract class CreatureBehaviour {
public abstract void TakeTurn (Action<TurnAction> response);
}

从此继承,PlayerControl类存储response回调以供以后决策。它的大部分内容都不相关,但最终它必须在玩家做某事时调用response

public class PlayerControl : CreatureBehaviour {
Action<TurnAction> responseCallback;
public override void TakeTurn(Action<TurnAction> response) {
responseCallback = response;
}
// Various UI callbacks that can send something to "responseCallback" when appropriate.
}

所有非玩家生物也需要能够发送回调。为了安全起见,我想确保回调始终被命中,因此我创建了一个抽象的NonPlayerControl类来确保响应:

public abstract class NonPlayerControl : CreatureBehaviour {
protected abstract TurnAction TurnResponse ();
public override void TakeTurn (Action<TurnAction> response) {
response (TurnResponse ());
}
}

有了这个,我所有的非玩家生物行为都可以从NonPlayerControl中衍生出来,并简单地实现TurnReponse()。编译器保证其所有脚本都将返回响应,而不是让回调挂起。请注意,PlayerControl无法实现TurnResponse(),因为它需要保证立即返回,这会阻止其他进程。

所以我想从NonPlayerControl派生其他类,也许从PlayerControl派生,但我不想意外地从CreatureBehaviour派生另一个类并冒着错过回调的风险。

我有什么办法可以"封印"CreatureBehaviour,让它只能有这两个直接的孩子,并防止任何其他孩子?如果没有,我可以在这里使用更好的模式吗?

您无法以"正常"的方式对此做任何事情,但是您可以考虑一种选择...

如果你只给你的CreatureBehavior类一个私有构造函数,然后在该类中嵌套PlayerBehaviorNonPlayerBehavior,他们将可以访问私有构造函数,但其他类不会......所以没有其他类可以从CreatureBehavior派生。

更简单的解决方案是:

  • CreatureBehavior文档中不应直接对其进行子类化
  • 编写单元测试以验证没有任何其他子

当然,这只能测试您的代码,而不是其他程序集中的代码。如果不需要其他程序集中的这些类,请将这些类设为内部而不是公共类。即使需要所有类都是公共的,也可以包含一个在PlayerBehaviorNonPlayerBehavior中实现的无操作抽象内部方法 - 这将阻止程序集外部的类从CreatureBehavior派生,因为它们无法实现内部抽象方法。

只是一个快速的想法,无需进一步测试:您可以使用泛型来执行限制吗?

public abstract class CreatureBehaviour<T> where T : IPlayerControl, INonPlayerControl {
// ...
}

然后按以下方式使用它:

public abstract class NonPlayerControl : CreatureBehaviour<NonPlayerControl>, INonPlayerControl {
// ...
}
public abstract class PlayerControl : CreatureBehaviour<PlayerControl>, IPlayerControl {
// ...
}

这是一种黑客攻击,但它可能适用于您的情况。

您不能禁止从 CreatureBehavoiur 类继承,但您可以通过组合内部、密封和受保护的访问来限制对 TakeTurn 方法的访问,并将其放置在单独的程序集中:

public  abstract class CreatureBehaviour
{
protected abstract void TakeTurn(Action<TurnAction> response);
}
public class PlayerControl : CreatureBehaviour
{
private Action<TurnAction> responseCallback;
protected override void TakeTurn(Action<TurnAction> response)
{
responseCallback = response;
}
internal void TurnByPlayer(Action<TurnAction> response)
{
TakeTurn(response);
}
// Various UI callbacks that can send something to "responseCallback" when appropriate.
}
public abstract class NonPlayerControl : CreatureBehaviour
{
protected abstract TurnAction TurnResponse();
protected override void TakeTurn(Action<TurnAction> response)
{
response(TurnResponse());
}
internal void TurnByNonPlayer(Action<TurnAction> response)
{
TakeTurn(response);
}
}
public sealed class CreatureStearing
{
public void Turn(PlayerControl control)
{
control.TurnByPlayer((action) => {});
}
public void Turn(NonPlayerControl control)
{
control.TurnByNonPlayer(action => {});
}
}

现在,您可以从其他程序集中的 PlayerControl、NonPlayerControl 甚至 CreatureBehavior 继承,但不能将 TakeTurn 方法用于除 PlayerControl 和 NonPlayerControl 之外的任何类集合,这些类位于单独的程序集中:

public class SomeTest1 : PlayerControl
{
protected override void TakeTurn(Action<TurnAction> response)
{
base.TakeTurn(response);
}
}
public class SomeTest2 : NonPlayerControl
{
protected override TurnAction TurnResponse()
{
throw new NotImplementedException();
}
protected override void TakeTurn(Action<TurnAction> response)
{
base.TakeTurn(response);
}
}
public class SomeTest3 : CreatureBehaviour
{
protected override void TakeTurn(Action<TurnAction> response)
{
throw new NotImplementedException();
}
}
....
var t1 = new SomeTest1();
var t2 = new SomeTest2();
var t3 = new SomeTest3();
var creatureStearing = new CreatureStearing();
creatureStearing.Turn(t1);
creatureStearing.Turn(t2);
creatureStearing.Turn(t3); // 'Cannot resolve method 'compile error here

当然,您可以通过声明程序集的内部访问权限来通过此限制,但它需要实现类似 CreatureStearing 的东西(嗯一些努力(,但在这种情况下,其他方肯定会知道这是一个黑客。

最新更新