Ich arbeite an einem API-Design, und es gibt etwas über Java und Polymorphismus, über das ich bisher nicht nachgedacht habe. Wenn ich eine API wie folgt zu erstellen:Java, Polymorphismus, statische Typisierung und das "QueryInterface" -Muster
interface FooFactory
{
public Foo getFoo();
}
interface Foo
{
public void sayFoo();
}
dann das einzige, was meine FooFactory
Implementierung darauf verlassen können zu schaffen ist eine Foo
Implementierung. Wenn ich mich entscheide, einige verbesserte Methoden zur Verfügung zu stellen, wie folgt aus:
interface EnhancedFoo extends Foo
{
public void patHeadAndRubBelly();
}
class EnhancedFooImpl implements EnhancedFoo
{
... implementation here ...
}
class EnhancedFooFactoryImpl implements FooFactory
{
@Override public EnhancedFoo getFoo() { return new EnhancedFooImpl(); }
}
und der einzige Weg, um meine API-Clients die EnhancedFoo
Schnittstelle verwenden können, ist, wenn sie eine Foo
Schnittstelle erhalten und versuchen, es als EnhancedFoo
zu werfen.
erinnere ich mich an die Art und Weise Microsoft COM in der IUnknown
Schnittstelle behandelt:
HRESULT QueryInterface(
[in] REFIID riid,
[out] void **ppvObject
);
Die Idee ist, dass Sie in einer GUID übergeben für die Schnittstelle, die Sie wollen, wenn es gelingt, sie wieder einen Zeiger, die Sie garantiert kann es sicher an die von Ihnen gesuchte Schnittstelle übertragen.
ich etwas ähnliches in einer Art und Weise mit typsicher Java tun könnte:
interface FooFactory
{
public <T extends Foo> T getFoo(Class<T> fooClass);
}
, wo ich eine Implementierung auf Anfrage zur Verfügung stellen, dass entweder eine Instanz des gewünschten Subschnittstelle zurückgibt, oder gibt null zurück, wenn keine vorhanden ist.
Meine Frage ist:
- ist das Query-Interface Muster sinnvoll?
- sollte ich nur Casting verwenden?
- oder ist der einzige richtige Weg, um Polymorphie in der API zu behandeln, um Clients zu beschränken, die fraglichen einfachen Methoden streng zu verwenden? (Zum Beispiel die Verfahren in
Foo
in meinem Beispiel und keineEnhancedFoo
Methoden)
Klarstellung: Die Fabrik Implementierung wird nicht von Client-Code bekannt sein würde. (Lassen Sie uns sagen, dass es Abhängigkeitsinjektion oder einige Service-Provider-Architektur wie Java ServiceLoader
oder NetBeans Lookup
verwendet.) Also als Client weiß ich nicht, was verfügbar ist. Die Fabrik könnte Zugang zu mehreren Foo
Derivaten haben und ich möchte, dass der Client in der Lage ist, einen gewünschten Feature-Set anzufordern, und entweder bekommt er das oder muss auf die Basis-Foo
-Funktionalität zurückgreifen.
Ich denke, der schwierige Teil für mich ist, dass es hier Laufzeitabhängigkeit gibt ... der reine statische typsichere Ansatz, bei dem alles zur Kompilierzeit festgelegt ist, bedeutet, dass ich nur auf diese grundlegende Foo
Funktionalität angewiesen bin. Dieser Ansatz ist mir vertraut, aber dann verliere ich auf mögliche verbesserte Funktionen. Der dynamischere/opportunistische Ansatz ist etwas, das diese Funktionen nutzen kann, aber ich bin mir nicht sicher, wie ich ein System, das es verwendet, richtig entwerfen kann.
Typinferenz wurde in Java 8 verbessert. – assylias
Hmm ... der Ausnahmeansatz ist normal für Python, scheint aber für Java ungeeignet. –
Ich würde sagen, es ist vollkommen angemessen - besser als Null-Überprüfung – Arkadiy