Sto lavorando a un progetto API e c'è qualcosa su Java e il polimorfismo a cui non ho pensato fino ad ora. Se creo un'API in questo modo:Java, Polymorphism, Static Typing e il pattern "QueryInterface"
interface FooFactory
{
public Foo getFoo();
}
interface Foo
{
public void sayFoo();
}
quindi l'unica cosa che il mio FooFactory
implementazione può essere invocata per fornire è un'implementazione Foo
. Se decido di fornire alcuni metodi avanzati, in questo modo:
interface EnhancedFoo extends Foo
{
public void patHeadAndRubBelly();
}
class EnhancedFooImpl implements EnhancedFoo
{
... implementation here ...
}
class EnhancedFooFactoryImpl implements FooFactory
{
@Override public EnhancedFoo getFoo() { return new EnhancedFooImpl(); }
}
e l'unico modo i miei clienti API possono utilizzare l'interfaccia EnhancedFoo
è se ottengono un'interfaccia Foo
e cercare di lanciarlo come il EnhancedFoo
.
Mi ricordo il modo in cui Microsoft ha gestito COM nell'interfaccia IUnknown
:
HRESULT QueryInterface(
[in] REFIID riid,
[out] void **ppvObject
);
L'idea è che si passa in un GUID per l'interfaccia che si desidera, in caso di successo, si torna un puntatore che vi garantisce puoi tranquillamente lanciarlo nell'interfaccia che stavi cercando.
avrei potuto fare qualcosa di simile in un modo typesafe con Java:
interface FooFactory
{
public <T extends Foo> T getFoo(Class<T> fooClass);
}
dove ho fornire un'implementazione su richiesta che o restituisce un'istanza della sottointerfaccia desiderato, oppure restituisce null se non è disponibile.
La mia domanda, è:
- è il modello QueryInterface ragionevole?
- devo usare la fusione?
- o è l'unico modo corretto per gestire il polimorfismo nell'API per limitare i client a utilizzare rigorosamente i metodi semplici in questione? (Ad esempio i metodi in
Foo
nel mio esempio e non le eventualiEnhancedFoo
metodi)
Chiarimento: L'implementazione fabbrica non sta per essere conosciuto da codice client. (Supponiamo che utilizzi l'iniezione di dipendenza o qualche architettura di provider di servizi come java's o NetBeans Lookup
). Quindi, come cliente, non so cosa sia disponibile. La fabbrica potrebbe avere accesso a diversi derivati e voglio che il client sia in grado di richiedere un set di funzionalità che desidera, e o lo ottiene, oppure dovrà ricorrere alla funzionalità di base .
Immagino che la parte difficile per me sia che qui c'è una dipendenza in fase di esecuzione ... l'approccio tipicamente statico puro in cui tutto è fissato in fase di compilazione significa che posso solo dipendere da quella funzionalità di base Foo
. Questo approccio mi è familiare, ma poi perdo le possibili funzionalità avanzate. Considerando che l'approccio più dinamico/opportunistico è qualcosa che può trarre vantaggio da queste caratteristiche, ma non sono sicuro del modo giusto di progettare un sistema che lo utilizza.
L'inferenza di tipo è stata migliorata in Java 8. – assylias
Hmm ... l'approccio di eccezione è normale per Python ma sembra inappropriato per Java. –
Direi che è perfettamente appropriato - meglio di null-checking – Arkadiy