Here is my use case. Say I have a game with two modes: CoolMode and NormalMode.
Both game modes are very different but share three common objects: Input, Score, and Judger. Each mode would have different implementations, but could be called from the main game using the same code.
interface Judger{
Score Judge(Input i);
}
abstract class Score {...}
abstract class Input {...}
I wanted to make sure for example, a CoolMode Input would not be passed to a NormalMode Judger. So my first solution was:
interface GameMode{}
interface Judger<T> where T:GameMode{
Score<T> Judge(Input<T> i);
}
abstract class Score<T> where T:GameMode {...}
abstract class Input<T> where T:GameMode {...}
interface NormalMode{}
class NormalJudger:Judger<NormalMode>{
Score<NormalMode> Judge(Input<NormalMode> i);
}
class NormalScore:Score<NormalMode>{...}
class NormalInput:NormalInput<NormalMode>{...}
...
The problem is that now NormalJudger can take any Input, not just NormalInput.
How can I force (at compile time) the classes {NormalJudger, NormalScore, NormalInput} to be used with each other?
The problem is that now NormalJudger can take any Input, not just NormalInput.
If by this you mean that someone could create their own implementation of Input<NormalMode>
and pass that to NormalJudger
, then really, you should design NormalJudger
so that it can judge any kind of Input<NormalMode>
. Isn't that why you have created Input
as an abstract class? This is the whole point of abstraction. Judge
should not care about what specific kind of Input
it is, as long as it is an Input<NormalMode>
. It seems like the NormalMode
type is just a "marker" for the type system to tell your types apart. Well, how about making it actually contain some data/logic, and make Judge
operate on it?
Or, you can just go the quick and dirty way, and parameterise the Judge
type differently. Rather than parameterise on the mode, parameterise on Score
and Input
:
public interface Judger<TScore, TInput>
where TScore: Score
where TInput: Input{
TScore Judge(TInput i);
}
public abstract class Score {}
public abstract class Input {}
interface NormalMode : GameMode {}
class NormalJudger:Judger<NormalScore, NormalInput>{
public NormalScore Judge(NormalInput i) {return null;}
}
class NormalScore:Score{}
class NormalInput:Input{}