I'm trying to build a GraphBot class that can answer different questions about about an associated graph such as path length, shortest path between two vertices, number of paths passing through a given vertex etc.
I'd like to be able to add new questions without changing any code inside the Bot, it is only responsible for receiving the question, delegating so some other algorithm to solve the question using the graph associated with the bot and returning the answer.
So something like this:
public class GraphBot {
private Graph graph;
public GraphBot(Graph graph) {
this.graph = graph;
}
public Answer ask(Question question) {
// delegate somehow to answer the question, providing the graph
// return an Answer object encapsulating the answer
}
}
public interface Answer {
public toPrintableOutput();
}
public interface Question {
// question methods go here... this is what I'm having trouble with
}
The trouble I'm having is that questions have associated conditions or parameters. For instance, the question "path length" between "A" and "B" has the question type ("path length") which is something all questions will have, but also the parameters "A" and "B". In comparison, the question "paths passing through vertex" will only have the parameter "C" representing the vertex.
I can't think of a way of presenting a uniform interface so that the system can be easily extended to handle numerous different questions with different numbers and types of parameters. I could have a QuestionSolver that was associated with each Question in which case it wouldn't matter if each Question had different attributes as the QuestionSolver could assert that the question is of a valid type but this would require a mapping of Question to QuestionSolver somewhere that would require updating whenever a new question is introduced.
What is the most extensible way of implementing this system so that new questions would not require tonnes of changes in loads of different classes? Really I want to be able to create an class implementing Question and maybe create a class that can solve that question and then have t开发者_运维技巧he GraphBot automatically able to handle that question.
What you can do is just have the ask()
method be part of the Question
interface, and take a Graph
. That way, each Question
has to know how to answer itself, and you don't have to design a Question
api that is future-extensible.
EDIT: Why is this a better design than making the GraphBot
be able to answer a Question
?
- Simplicity: As you're finding out, it's very hard to design a
Question
API that will take every possible data required into account for any question as well as figuring out how to compile that into a dynamic question asking algorithm. It's much simpler to have custom code perQuestion
that will know what to do with the publicly knownGraph
data. - Depndencies: Your initial approach makes the relatively stable
GraphBot
dependent on the definitely unstableQuestion
. My approach reverses this dependency, making it all much simpler, which leads back into the last point. - Maintenance: I guarantee you wouldn't be able to predict everything the
Question
class needs to make available. When (not if) you come across a change you need to make in theQuestion
interface, every implementer will need to change. This will make your future you (or some other developer) hate your present you with the fiery passion of a thousand suns if there are more than a fewQuestion
implementers.
In the end, I can't think of a convincing reason to not make a question answer itself. Yes, it's arguable that it doesn't make a lot of sense from a purely abstract "does this align with the real world" point of view, but it's both practical and not unreasonable for a question to handle it's own answering. One thing to remember about OOP is that the similarities with the real world are for convenience, not as a rule. Don't let yourself be straitjacketed by them.
So now it would be like this:
public class GraphBot {
private Graph graph;
public GraphBot(Graph graph) {
this.graph = graph;
}
public Answer ask(Question question) {
return question.ask(graph);
}
}
public interface Answer {
public toPrintableOutput();
}
public interface Question {
public Answer ask(Graph graph);
}
精彩评论