First answer:
I wonder whether this pattern that I have frequently used is common [...]
I have encounterd it widely, in several APIs. I didn't mind using when it was documented (you need to run()
before you get()
the results, etc), though sometimes the input objects were many, and hard to setup properly. However very few documentations were complete, and if they are at all necessary, then it could be done better.
However when I had to extend it, it proved very troublesome (required Ctor is messy, results sometimes hard to extend, Status unknown (has it run()
yet?), some result objects might be unavailbable (for good reasons)...
Second answer:
I wonder whether this pattern that I have frequently used [...] has a name:
As others said, looks like Command Pattern.
Now for a few comments on extendability:
(Take with a grain of salt, may not apply to your application.)
That is good. (containment of an algorithm to an entity).
But that is not very extensible - only through inheritance, which is restrictive. Too many APIs force inheritance because they believe they are the heart of your application. What if I want to extend something else to do the job, like a Map
?
What would be better is:
- A
solver
class can be interacted with through an interface
- One or several concrete classes provide exchangeable
algorithm
implementations of the solver
When I want a solver instance, I can create one from scratch by extending the interface with complete freedom, or reuse some known implementation through (multiple) inheritance.
- its parameters are passed via the constructor of the class
That's an implementation detail. Why force someone to handle objects, for which they may not care? How do you even enforce Constructors to have specific parameters? What if the initial state is complex and multi-faceted, and requires a Builder?
No pattern should require a specific constructor (except en empty one?). Also, beware, constructors shouldn't do too much computation.
- calling a parameter-less run method triggers the computation
- How do you ensure it isn't called several times?
- Or that it is even called before the
getResult()
methods?
- If I run() again, do I get the same results?
- Or does the computation happen again?
- the result(s) are fetched by calling getter methods
- But when are they ready? The result object needs to be
null
-able (ugh), an OptionalResult
, or have a isAvailable()
method on it.
- Can the result become stale()?
- How can I share these results without sharing the whole algorithm?
- What do I do with the
Algorithm
instance afterwards? is it re-usable? Can I reset()
it? I might have been costly to set up.
I have found it it MUCH clearer everywhere I found it, to have (JAVA here):
public interface Solver {
<? extends SolverResult> run(SolverInput input);
}
In here, implementations can vary, each instance of solver has a completely internal state which may speed it up (like a cache) for later use, but is distinct from the SovlerResult object, which is what I wanted. The SolverInput can be created as I want, and SolverResult can be a specialized result class of the solver instance, capable of giving better insight (with additional getComputationQuality()
, getConvergenceRate()
etc.).
I think separating the input, the solver, and the output is much more flexible. Design-patterns should not be an enforcement tool, but more a facilitator tool. It even allows your design patter by implementing all three interfaces at once (not that I recommended it).