Ho una classe che trasforma un modello complesso, ad esempio un albero sintattico astratto o un modello intermedio. Il modello può essere valido, non valido o parzialmente non valido, ovvero contiene errori ma alcune parti di esso sono valide e possono essere ulteriormente elaborate.
La segnalazione degli errori più semplice consiste nell'usare eccezioni:
public class Transformer {
public TargetModel transform(SourceModel model)
throws InvalidModelException {
// ...
}
}
Ovviamente questo non consente di riportare più errori (almeno se non si allegano ulteriori informazioni all'eccezione) e le eccezioni dovrebbero essere per situazioni eccezionali.
Martin Fowler ha affrontato questo problema nel suo articolo Sostituendo le eccezioni di lancio con la notifica nelle convalide . Se applichi il suo metodo al problema, hai qualcosa di simile a questo:
public abstract class Problem {
// ...
}
public final class ProblemA {
// ...
}
public final class ProblemB {
// ...
}
public class Transformer {
public static final class TransformationResult {
private Collection<Problem> problems;
private Optional<TargetModel> targetModel;
// ...
}
public TransformationResult transform(SourceModel model) {
// ...
}
}
Quindi puoi utilizzare il pattern visitor o instanceof
checks per distinguere gli errori. È un compromesso tra sicurezza del tipo e verbosità.
Un'altra possibile soluzione sarebbe quella di utilizzare il modello di osservatore:
public class Transformer {
public interface ProblemListener {
public void onProblemA(...);
public void onProblemB(...);
// ...
}
public void addProblemListener(ProblemListener listener) {
// ...
}
public void removeProblemListener(ProblemListener listener) {
// ...
}
public TargetModel transform(SourceModel model) {
// ...
}
}
L'utilizzo del pattern di osservatore ha il vantaggio che non è necessario accumulare gli errori in memoria e che non sono necessari eccessivi controlli di instanceof
o visitatori. D'altra parte oscura il flusso di controllo anche più del modello di visitatore.
Penso che tutte le soluzioni non portino a codice leggibile. In un linguaggio funzionale e con una memoria sufficiente, utilizzerei la seconda soluzione, ma nella corrispondenza del modello strutturale Java implica o controlli instanceof
o l'utilizzo del modello visitatore che sono entrambi dettagliati. C'è qualcosa che ho trascurato? O implicazioni che non ho considerato? Qual è la tua esperienza con una qualsiasi delle soluzioni?