2009-06-23 22 views
2

Ich entwerfe ein Framework, das Benutzer erweitern können. Grundsätzlich werde ich ihnen eine Reihe von Schnittstellen zur Verfügung stellen, die sie umsetzen können, und mein „Manager“ Klasse wird ihre Implementierungen wie folgt aufrufen:Ist es in Ordnung, downcast?

abstract1 = factory.GetConcreteExtension1() 
abstract2 = factory.GetConcreteExtension2() 
abstract1.DoSomething(abstract2) 

Wo ist mein Kunde die konkrete Version der Abstraktionen implementiert. Sie könnten jedoch entscheiden, Daten zu concrete2 hinzuzufügen, die nicht in der abstract2-Schnittstelle sind. Dies wird sie zwingen, abstract2 zu concrete2 in concrete.DoSomething Umsetzung zu reduzieren.

Das sieht wie ein Code-Geruch aus, weil ich sie zwinge, eine Methode (DoSomething) zu implementieren, die in ihrer Signatur abstract1 erwartet, aber tatsächlich nur concrete1 bekommen kann (plus ich zwinge sie, das Casting zu schreiben).

Ich kann keine Lösung finden, die beide Vertrag gut definiert und erlaubt meinem Framework, den Prozess selbst zu verwalten. Irgendwelche Ideen?

Antwort

1

Es hängt von der Sprache ab, die Sie verwenden, aber in C# könnten Sie beispielsweise Templates mit einer Einschränkung verwenden, um Ihnen Typsicherheit zu geben und gleichzeitig zu gewährleisten, dass die Klasse von Ihrer abstrakten Basisklasse abgeleitet werden muss.

+0

Aber in diesem Fall geht die Verantwortung von der aufgerufenen Funktion zum Aufrufer - sie muss den richtigen Typ in den generischen Parameter eingeben. Es ist also immer noch eine seltsame api - es sieht aus wie es ist generisch, aber tatsächlich erlaubt nur einen Typ ... –

+0

@Yaron - es ist, aber auf der anderen Seite ist dies ein bisschen wie eine seltsame API zu Beginn. Die DoSomething() -Methode wird deklariert, um einen "abstract2" als Parameter zu nehmen, aber die Logik darin kann erfordern, dass sie Dinge tut, die ein "abstract2" nicht kann (daher die Notwendigkeit eines Downcast). Ich stimme Hans hier zu, dass es das Beste wäre, sicherzustellen, dass die Klasse "abstract2" über das erforderliche Verhalten verfügt, was einen Downcast überflüssig macht. –

+0

Das würde funktionieren, wenn ich den ganzen Code implementieren würde. Dies ist jedoch ein Framework, das Benutzer erweitern werden. Lassen Sie mich Ihnen ein konkretes Beispiel geben: - Entwickler werden ein UI-Formular implementieren, das einige Objekte basierend auf Benutzereingaben zurückgibt. - Zusätzliche Objekte, die von demselben Entwickler implementiert werden, erfordern als Eingabe den von der UI zurückgegebenen. Mein Framework ist dafür verantwortlich, sie aufzurufen und ihnen die Daten zu geben. Dieses Objekt enthält Daten, die ich beim Entwerfen der Schnittstelle nicht kennen kann. –

2

So wird das DoSomething von abstract1 mit dem Parameter abstract2 aufgerufen und Sie möchten von der DoSomething-Methode zu den Daten von abstract2 gelangen?

In diesem Fall scheint es mir, dass es besser wäre, abstract2-Verhalten zu geben, so dass abstract1 mit ihm durch das Verhalten von abstract2 und nicht durch seine Daten interagiert.

+0

+1. Genau richtig. abstract1.DoSomething (abstract2) sollte abstract2.SomeInterfaceMethodOfAbstract2() aufrufen (was in concrete2 implementiert ist), um Dinge zu erledigen, anstatt abstract2 auf concrete2 downcast und direkt auf die Daten zuzugreifen. –

+0

Ich meine, dass abstract1.DoSomething (abstract2) kann eine beliebige Anzahl von Methoden in der Schnittstelle abstract2 aufrufen, sofern sie in der Schnittstelle sind. Wenn das gewünschte Verhalten jedoch von den konkreten Typen von abstract1 und abstract2 abhängt, dann ist die sauberste Lösung, die ich kenne, das Anfordern von "benannten" Interfaces, wie zB QueryInterface() in COM. Hier ist ein C++ - Ansatz: http://StackOverflow.com/Questions/851323/reflection-in-C/853130#853130 –

+0

Eigentlich ist es nicht abstrakt2 Verhalten wir sind interessiert, aber seine Daten. Daher benötigt abstract1 Zugriff auf concrete2.SomeData - aber ich kenne SomeData nicht in meinem Framework, so dass ich es nicht zur abstrakten API hinzufügen kann. Wie für benannte Schnittstellen - das sieht immer noch wie verkleidet in der Verkleidung aus ... –

Verwandte Themen