Al suggerimento di @ amon, ecco una risposta più monadica. È una versione molto ridotta, in cui devi accettare alcune ipotesi:
-
la funzione "unit" o "return" è il costruttore della classe
-
l'operazione "bind" avviene in fase di compilazione, quindi è nascosta dall'invocazione
-
anche le funzioni "azione" sono legate alla classe in fase di compilazione
-
sebbene la classe sia generica e avvolga qualsiasi classe arbitraria E, penso che in questo caso sia davvero eccessivo. Ma l'ho lasciato in questo modo come esempio di cosa potresti fare.
Con queste considerazioni, la monade si traduce in una fluente classe wrapper (sebbene tu stia rinunciando alla grande flessibilità che otterresti in un linguaggio puramente funzionale):
public class RepositoryLookup<E> {
private String source;
private E answer;
private Exception exception;
public RepositoryLookup<E>(String source) {
this.source = source;
}
public RepositoryLookup<E> fetchElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookup(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public RepositoryLookup<E> orFetchSimilarElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookupVariation(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public RepositoryLookup<E> orFetchParentElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookupParent(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public boolean failed() {
return exception != null;
}
public Exception getException() {
return exception;
}
public E getAnswer() {
// better to check failed() explicitly ;)
if (this.exception != null) {
throw new IllegalArgumentException(exception);
}
// TODO: add a null check here?
return answer;
}
}
(questo non verrà compilato ... alcuni dettagli sono lasciati incompiuti per mantenere il campione piccolo)
E l'invocazione sarebbe simile a questa:
Repository<String> repository = new Repository<String>(x);
repository.fetchElement().orFetchParentElement().orFetchSimilarElement();
if (repository.failed()) {
throw new IllegalArgumentException(repository.getException());
}
System.err.println("Got " + repository.getAnswer());
Nota che hai la flessibilità di comporre le operazioni "fetch" come preferisci. Si fermerà quando otterrà una risposta o un'eccezione diversa da quella non trovata.
L'ho fatto molto velocemente; non è giusto, ma si spera che trasmetta l'idea