Ich schreibe eine Java-Implementierung eines Kartenspiels, also habe ich eine spezielle Art von Sammlung erstellt, die ich als Zone bezeichne. Alle Änderungsmethoden von Javas Sammlung werden nicht unterstützt, es gibt jedoch eine Methode in der Zonen-API, move(Zone, Card)
mit der eine Karte von der angegebenen Zone zu sich selbst verschoben wird (mithilfe von paketprivaten Techniken). Auf diese Weise kann ich sicherstellen, dass keine Karten aus einer Zone genommen werden und einfach verschwinden. Sie können nur in eine andere Zone verschoben werden.
Meine Frage ist, wie notwendig ist diese Art der defensiven Codierung? Es ist "richtig" und es fühlt sich wie die richtige Praxis an, aber es ist nicht so, dass die Zone-API jemals Teil einer öffentlichen Bibliothek sein wird. Es ist nur für mich, also ist es so, als würde ich meinen Code vor mir selbst schützen, wenn ich wahrscheinlich effizienter sein könnte, wenn ich nur Standard-Sammlungen verwende.
Wie weit sollte ich mit dieser Zonenidee gehen? Kann mir jemand einen Rat geben, inwieweit ich darüber nachdenken sollte, die Verträge in den von mir geschriebenen Klassen beizubehalten, insbesondere für solche, die nicht wirklich öffentlich verfügbar sein werden?
quelle
Antworten:
Ich werde das Designproblem nicht ansprechen - nur die Frage, ob die Dinge in einer nicht öffentlichen API "richtig" gemacht werden sollen.
Genau darum geht es. Vielleicht gibt es Codierer, die sich an die Nuancen jeder Klasse und Methode erinnern, die sie jemals geschrieben haben, und die sie nie versehentlich mit dem falschen Vertrag aufrufen. Ich bin keiner von ihnen. Ich vergesse oft, wie Code, den ich geschrieben habe, innerhalb weniger Stunden nach dem Schreiben funktionieren soll. Nachdem Sie denken , dass Sie es richtig einmal bekommen haben, neigen dazu , Ihren Geist Gänge , um das Problem zu schalten Sie gerade arbeiten jetzt .
Sie haben Werkzeuge, um das zu bekämpfen. Diese Tools umfassen (in keiner bestimmten Reihenfolge) Konventionen, Komponententests und andere automatisierte Tests, Voraussetzungsprüfungen und Dokumentationen. Ich selbst habe Unit-Tests für von unschätzbarem Wert befunden, da Sie beide gezwungen sind, über die Verwendung Ihres Vertrags nachzudenken und später zu dokumentieren, wie die Benutzeroberfläche entworfen wurde.
quelle
Ich befolge normalerweise einige einfache Regeln:
IllegalArgumentException
. B. ).assert input != null
. B. ).Wenn ein Client wirklich daran interessiert ist, wird er immer einen Weg finden, Ihren Code schlecht zu machen. Sie können es zumindest immer durch Reflektion tun. Aber das ist das Schöne an Vertragsgestaltung . Sie stimmen einer solchen Verwendung Ihres Codes nicht zu und können daher nicht garantieren, dass er in solchen Szenarien funktioniert.
Wenn
Zone
die Klasse in Ihrem speziellen Fall nicht von Außenstehenden verwendet und / oder auf sie zugegriffen werden soll, machen Sie sie entweder paketprivat (und möglicherweisefinal
), oder verwenden Sie vorzugsweise die Sammlungen, die Java Ihnen bereits zur Verfügung stellt. Sie sind getestet und Sie müssen das Rad nicht neu erfinden. Beachten Sie, dass Sie dadurch nicht daran gehindert werden, Zusicherungen im gesamten Code zu verwenden, um sicherzustellen, dass alles wie erwartet funktioniert.quelle
Defensive Programmierung ist eine sehr gute Sache.
Bis es anfängt, dem Schreiben von Code im Wege zu stehen. Dann ist es keine so gute Sache.
Ein bisschen pragmatischer sprechen ...
Es hört sich so an, als ob Sie kurz davor stehen, Dinge zu weit zu bringen. Die Herausforderung (und die Antwort auf Ihre Frage) besteht darin, die Geschäftsregeln oder Anforderungen des Programms zu verstehen.
Am Beispiel Ihrer Kartenspiel-API gibt es einige Umgebungen, in denen alles, was getan werden kann, um Betrug zu verhindern, von entscheidender Bedeutung ist. Möglicherweise sind große Beträge an echtem Geld erforderlich. Es ist daher sinnvoll, eine große Anzahl von Schecks einzulegen, um sicherzustellen, dass kein Betrug auftritt.
Auf der anderen Seite müssen Sie die SOLID-Prinzipien beachten, insbesondere die Einzelverantwortung. Die Containerklasse zu bitten, effektiv zu prüfen, wohin die Karten gehen, kann ein bisschen viel sein. Möglicherweise ist es besser, eine Audit- / Controller-Schicht zwischen dem Kartencontainer und der Funktion zu haben, die die Verschiebungsanforderungen empfängt.
Im Zusammenhang mit diesen Bedenken müssen Sie verstehen, welche Komponenten Ihrer API öffentlich zugänglich (und damit anfällig) sind und welche weniger privat und anfällig sind. Ich bin kein Befürworter einer "harten Außenbeschichtung mit einer weichen Innenseite", aber die beste Rendite Ihrer Bemühungen besteht darin, die Außenseite Ihrer API zu härten.
Ich denke nicht, dass der beabsichtigte Endbenutzer einer Bibliothek so kritisch gegenüber einer Entscheidung ist, wie viel defensive Programmierung Sie implementiert haben. Sogar mit Modulen, die ich für meinen eigenen Gebrauch schreibe, habe ich trotzdem ein gewisses Maß an Überprüfung vorgenommen, um sicherzustellen, dass ich in Zukunft keinen versehentlichen Fehler beim Aufrufen der Bibliothek gemacht habe.
quelle
Defensive Codierung ist nicht nur für öffentlichen Code eine gute Idee. Es ist eine großartige Idee für jeden Code, der nicht sofort weggeworfen wird. Sicher, Sie wissen, wie es jetzt heißen soll , aber Sie haben keine Ahnung, wie gut Sie sich an diese sechs Monate erinnern werden, wenn Sie zum Projekt zurückkehren.
Die grundlegende Syntax von Java bietet Ihnen im Vergleich zu einer niedrigeren oder interpretierten Sprache wie C oder Javascript eine Menge eingebauten Schutz. Vorausgesetzt, Sie benennen Ihre Methoden eindeutig und verfügen nicht über eine externe "Methodensequenzierung", können Sie wahrscheinlich einfach Argumente als korrekten Datentyp angeben und vernünftiges Verhalten einschließen, wenn ordnungsgemäß eingegebene Daten immer noch ungültig sein können.
(Abgesehen davon, wenn Karten immer in der Zone sein müssen, ist es meiner Meinung nach günstiger, wenn alle im Spiel befindlichen Karten von einer globalen Sammlung auf Ihr Spielobjekt referenziert werden und Zone eine Eigenschaft von ist Aber da ich nicht weiß, was Ihre Zonen tun, außer Karten zu halten, ist es schwer zu wissen, ob das angemessen ist.)
quelle
CardDescriptor
eine Karte, deren Position, Status (Bild oben / unten) oder sogar eine Rotation für Spiele, die sich darum kümmern. Dies sind alles veränderbare Eigenschaften, die die Identität einer Karte nicht verändern.Erstellen Sie zunächst eine Klasse, die eine Liste von Zonen enthält, damit Sie keine Zone oder die darin enthaltenen Karten verlieren. Sie können dann überprüfen, ob sich eine Übertragung in Ihrer Zonenliste befindet. Diese Klasse wird wahrscheinlich eine Art Singleton sein, da Sie nur eine Instanz benötigen. Möglicherweise möchten Sie jedoch später mehrere Zonen festlegen. Lassen Sie Ihre Optionen also offen.
Zweitens: Lassen Sie Zone oder ZoneList keine Collection oder etwas anderes implementieren, es sei denn, Sie erwarten, dass Sie dies benötigen. Das heißt, wenn eine Zone oder ZoneList wird etwas übergeben werden, der eine Sammlung erwartet, dann es umzusetzen. Sie können eine Reihe von Methoden deaktivieren, indem sie eine Ausnahme auslösen (UnimplementedException oder ähnliches) oder indem sie einfach nichts tun. (Denken Sie gut nach, bevor Sie die zweite Option verwenden. Wenn Sie dies tun, weil es einfach ist, werden Sie feststellen, dass Sie Fehler vermissen, die Sie möglicherweise frühzeitig entdeckt haben.)
Es gibt echte Fragen darüber, was "richtig" ist. Aber sobald Sie herausgefunden haben, was es ist, möchten Sie die Dinge auf diese Weise tun. In zwei Jahren haben Sie all das vergessen, und wenn Sie versuchen, den Code zu verwenden, werden Sie über den Kerl, der ihn auf eine so kontraproduktive Art und Weise geschrieben und nichts erklärt hat, wirklich verärgert.
quelle
Bei der defensiven Codierung im API-Design geht es im Allgemeinen um die Validierung von Eingaben und die sorgfältige Auswahl eines geeigneten Fehlerbehandlungsmechanismus. Dinge, die andere Antworten erwähnen, sind ebenfalls erwähnenswert.
Darum geht es in Ihrem Beispiel eigentlich nicht. Sie beschränken Ihre API-Oberfläche aus einem ganz bestimmten Grund. Wie GlenH7 erwähnt, sollten Sie, wenn der Kartensatz in einem tatsächlichen Spiel verwendet werden soll, zum Beispiel mit einem ('benutzten' und 'unbenutzten') Deck, einem Tisch und Händen, auf jeden Fall Schecks anbringen, um dies sicherzustellen Karte aus dem Set ist einmal und nur einmal vorhanden.
Dass Sie dies mit "Zonen" gestaltet haben, ist eine willkürliche Wahl. Abhängig von der Implementierung (eine Zone kann im obigen Beispiel nur eine Hand, ein Deck oder ein Tisch sein) kann es sich durchaus um eine gründliche Planung handeln.
Diese Implementierung klingt jedoch wie ein abgeleiteter Typ eines mehr
Collection<Card>
ähnlichen Kartensatzes mit einer weniger restriktiven API. Wenn Sie zum Beispiel einen Handwertrechner oder eine KI bauen möchten, möchten Sie sicherlich frei wählen können, über welche und wie viele Karten Sie iterieren.Es ist also gut, eine solche einschränkende API freizulegen, wenn das einzige Ziel dieser API darin besteht, sicherzustellen, dass sich jede Karte immer in einer Zone befindet.
quelle