Angenommen, Sie hätten die folgende Schnittstelle
public interface IUserRepository
{
User GetByID(int userID);
}
Wie würden Sie Implementierer dieser Schnittstelle dazu zwingen, eine Ausnahme auszulösen, wenn ein Benutzer nicht gefunden wird?
Ich vermute, dass es nicht möglich ist, nur mit Code zu arbeiten. Wie würden Sie Implementierer dazu zwingen, das beabsichtigte Verhalten zu implementieren? Sei es durch Code, Dokumentation usw.?
In diesem Beispiel wird erwartet, dass die konkrete Implementierung a auslöst UserNotFoundException
public class SomeClass
{
private readonly IUserRepository _userRepository;
public SomeClass(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void DisplayUser()
{
try
{
var user = _userRepository.GetByID(5);
MessageBox.Show("Hello " + user.Name);
}
catch (UserNotFoundException)
{
MessageBox.Show("User not found");
}
}
}
c#
.net
object-oriented-design
interfaces
Matthew
quelle
quelle
Antworten:
Dies ist eine Sprachfunktion, die in C # absichtlich weggelassen wurde . Ganz einfach, es ist durchaus möglich, dass ein
IUserRepository.GetByID
Fehler aus einem anderen Grund als dem nicht gefundenen Benutzer fehlschlägt. Sie möchten also keinen bestimmten Fehler benötigen, wenn dies nicht möglich ist. Sie haben zwei Möglichkeiten, wenn Sie dieses Verhalten aus irgendeinem Grund erzwingen möchten:User
Klasse so, dass sie selbst die Ausnahme auslöst, wenn sie nicht ordnungsgemäß initialisiert wurde.IUserRepository
diesen expliziten Test für dieses Verhalten.Beachten Sie, dass keine dieser Optionen "in die Dokumentation aufnehmen" ist. Im Idealfall sollten Sie dies trotzdem tun, insbesondere in der Dokumentation können Sie erklären, warum Sie einen bestimmten Fehlertyp erzwingen möchten.
quelle
Es gibt keine Möglichkeit, eine Implementierung zum Auslösen einer Ausnahme über eine Schnittstelle zu verlangen , selbst in Sprachen wie Java, in denen Sie deklarieren können, dass eine Methode eine Ausnahme auslösen könnte.
Es kann eine Möglichkeit geben, sicherzustellen (bis zu einem gewissen Grad, aber nicht absolut), dass eine Ausnahme ausgelöst wird. Sie können eine abstrakte Implementierung Ihrer Schnittstelle erstellen. Sie können die
GetUser
Methode dann als final in der abstrakten Klasse implementieren und mithilfe des Strategiemusters ein anderes, geschütztes Mitglied der Unterklasse aufrufen und eine Ausnahme auslösen, wenn etwas anderes als ein gültiger Benutzer zurückgegeben wird (z. B. null). Dies kann immer noch zum Erliegen kommen, wenn der andere Entwickler beispielsweise einen Nullobjekttyp zurückgibtUser
, aber er müsste wirklich arbeiten, um die Absicht hier zu untergraben. Sie könnten auch nur Ihre Schnittstelle neu implementieren, auch schlecht, so dass Sie in Betracht ziehen könnten, die Schnittstelle vollständig durch die abstrakte Klasse zu ersetzen.(Ähnliche Ergebnisse können mit der Delegierung erzielt werden, anstatt mit einem Verpackungsdekorateur eine Unterklasse zu erstellen.)
Eine andere Option könnte darin bestehen, eine Konformitätstestsuite zu erstellen, die der gesamte implementierende Code übergeben muss, um aufgenommen zu werden. Wie effektiv dies ist, hängt davon ab, wie viel Kontrolle Sie über die Verknüpfung des anderen Codes mit Ihrem Code haben.
Ich stimme auch anderen zu, dass eine klare Dokumentation und Kommunikation selbstverständlich sind, wenn eine solche Anforderung erwartet wird, aber im Code nicht vollständig durchgesetzt werden kann.
Codebeispiele:
Unterklassenmethode:
Dekorationsmethode:
Ein letzter kurzer Punkt, den ich vergessen möchte, ist, dass Sie sich durch eine dieser Aktionen an eine spezifischere Implementierung binden, was bedeutet, dass Implementierungsentwickler so viel weniger Freiheit erhalten.
quelle
Wenn Sie mit den Entwicklern zusammenarbeiten, die das Verhalten zum Auslösen von Ausnahmen implementieren möchten, können Sie möglicherweise Unittests (für sie) schreiben, um zu überprüfen, ob eine Ausnahme ausgelöst wird, wenn Sie versuchen, einen Benutzer zu finden, der nicht vorhanden ist.
Sie müssen daher wissen, welche Methoden getestet werden sollen. Ich bin mit C # nicht vertraut, aber Sie könnten Reflection verwenden, um nach allen Klassen zu suchen, die das IUserRepository implementieren, und diese automatisch zu testen. Selbst wenn ein Entwickler eine weitere Klasse hinzufügt, wird diese beim Ausführen der Tests getestet.
Dies ist jedoch nur dann möglich, wenn Sie wirklich darunter leiden, dass Entwickler die Implementierungen falsch ausführen, mit denen Sie arbeiten müssen. Theoretisch dienen Schnittstellen nicht dazu zu definieren, wie die Geschäftslogik implementiert werden muss.
Ich freue mich auf weitere Antworten, da dies wirklich eine interessante Frage ist!
quelle
Das kannst du nicht. Das ist die Sache mit Schnittstellen - sie ermöglichen es jedem, sie jederzeit zu implementieren. Es gibt unendlich viele mögliche Implementierungen für Ihre Schnittstelle, und Sie können keine davon erzwingen, korrekt zu sein. Durch die Auswahl einer Schnittstelle haben Sie Ihr Recht auf Durchsetzung der Verwendung einer bestimmten Implementierung im gesamten System verwirkt. Was Sie haben, ist nicht einmal eine Schnittstelle, sondern eine Funktion. Sie schreiben Code, der Funktionen weitergibt.
Wenn Sie diesen Weg gehen müssen, müssen Sie lediglich die Spezifikation, der die Implementierungen entsprechen müssen, klar dokumentieren und darauf vertrauen, dass niemand diese Spezifikation absichtlich verletzt.
Die Alternative besteht darin, einen abstrakten Datentyp zu haben, der in eine Klasse in Java / C # -ähnlichen Sprachen übersetzt wird. Das Problem ist, dass Mainstream-Sprachen ADTs nur schwach unterstützen und Sie die Implementierung der Klasse ohne Dateisystem-Tomfoolery nicht ändern können. Wenn Sie jedoch bereit sind, damit zu leben, können Sie sicherstellen, dass nur eine Implementierung im System vorhanden ist. Dies ist ein Fortschritt gegenüber Schnittstellen, bei denen Ihr Code jederzeit beschädigt werden kann, und wenn dies der Fall ist, müssen Sie herausfinden, welche Implementierung der Schnittstelle Ihren Code beschädigt hat und woher er stammt.
BEARBEITEN : Beachten Sie, dass Sie selbst mit IL-Hacks nicht die Richtigkeit bestimmter Aspekte einer Implementierung sicherstellen können. Beispielsweise wird implizit angenommen, dass
GetByID
entweder ein Wert zurückgegeben oder eine Ausnahme ausgelöst werden muss. Jemand könnte eine Implementierung schreiben, die einfach in eine Endlosschleife geht und beides nicht tut. Sie können zur Laufzeit nicht beweisen, dass sich der Code für immer wiederholt. Wenn Sie dies schaffen, haben Sie das Halteproblem gelöst. Möglicherweise können Sie triviale Fälle erkennen (z. B. a erkennenwhile (true) {}
), aber keinen beliebigen Code.quelle
Es gibt keine Möglichkeit zu 100% Kraft des Verhalten , das Sie wollen, aber unter Verwendung von Code Verträge könnte man sagen , dass Implementierungen nicht null zurückgeben muß, und vielleicht , dass das Benutzerobjekt die ID übergeben hat (so ein Objekt mit Benutzer - ID 0 würde scheitern , wenn 1 übergeben wurde ). Darüber hinaus kann es hilfreich sein, zu dokumentieren, was Sie von Implementierungen erwarten.
quelle
Dies ist wahrscheinlich sehr spät, um diese Frage zu beantworten, möchte aber meine Gedanken dazu teilen. Wie aus allen Antworten hervorgeht, ist es nicht möglich, die Einschränkung mit der Schnittstelle zu erzwingen. Auch mit Hacks wäre das schwierig.
Eine Möglichkeit, dies zu erreichen, die wahrscheinlich spezifisch für .NET ist, besteht darin, die Benutzeroberfläche wie folgt zu gestalten:
Die Task erzwingt nicht das Auslösen bestimmter Ausnahmetypen, bietet jedoch die Möglichkeit, die Absicht aller Implementierer dieser Schnittstelle zu vermitteln. Aufgaben in .NET haben ein bestimmtes Verwendungsmuster, z. B. Task.Ergebnis für den Zugriff auf das Ergebnis, Task.Exception, wenn eine Ausnahme vorliegt. Außerdem kann der Benutzer es auch asynchron verwenden.
quelle
Mit einer Schnittstelle ist das nicht möglich, da viele andere bereits geantwortet haben.
Aber Sie können das mit einer abstrakten Basisklasse tun:
quelle