I was interested in solving several unrelated problems using generic graph search techniques, so after some fiddling around I came up with the following design: have each problem implement a generic "Problem" interface which has an initial state, and several methods that take in states and moves, and return new states and moves. For example, I can
applyMove(state, move), or check
isGoal(state). All of the problem specific details are then in one place, and the generic algorithms can pass around states and moves as generic "tokens" that they don't really care about, using the Problem instance to evaluate their properties. The Problems are also consequently stateless (or what state they have is immutable), which makes their behaviour very easy to reason about.
Move interface. This already has some problems, as if my state is just some data structure, then I need to extend or wrap the structure, but that's basically fine.
The next problem is that the tokens are implementation specific, so every time they're given to the Problem, they need to be cast into what they actually are. Lots of casting strikes me as a red flag, so this seems like a bad (certainly unpleasant) approach.
To avoid all the casting, I briefly considered something involving tons of templates (State<ChessProblem>, Move<ChessProblem>), but that seemed like a headache which might not even fix the problem.
Is there some "proper"/standard way to accomplish this in stricter type systems? Is this just a bad pattern to use in them (or a bad pattern overall)? Any insights would be appreciated. Also, while I am using Java, if other static/strict languages have novel tools for this sort of thing, I'd love to hear about them.