Ich entwickle ein Programmierspiel, bei dem Spieler Zugang zu einer abstrakten Klasse haben und sie erweitern können, um das Verhalten eines Roboters zu steuern. Da es sich um ein Programmierspiel handelt, versuche ich, meine Spielinfrastruktur so zu schützen, dass die Spieler sich nicht mit dem Spiel auseinandersetzen, statt nur mit der Klasse, die ich ihnen gebe; Dafür mache ich die meisten meiner Klassen final
, aber jetzt kann ich sie nicht in meinen Komponententests verspotten (mockito + testNG).
Also habe ich mich gefragt, wie kann ich das umgehen? Gibt es eine Möglichkeit, die Klassen zum Testen nicht-final zu lassen und dann irgendwie ' final
-ize' zu einem späteren Zeitpunkt des Build-Zyklus (ich benutze Maven, falls es für die Antwort relevant ist). Ich möchte keine weitere externe Bibliothek hinzufügen oder meine spöttische Bibliothek ändern.
Wenn das nicht möglich ist, dann eine zweite Frage: macht eine Klasse final
wirklich sicher? Ich sah einige Bibliotheken, die den final
Klassifikator aus Bytecode entfernen könnten, was mich denken lässt, dass final
ist nutzlos, wenn es aus einer bereits kompilierten Klasse entfernt werden kann
Erstens können Sie nicht verhindern, dass Leute sich mit Ihrem Spiel anlegen, indem Sie Dinge als "endgültig" deklarieren. Es gibt Reflektionstricks und ganze Code-Generierungsbibliotheken, die es einfach nur unbequem machen, darüber hinwegzukommen.
Verliere also das Finale, und dann kannst du jmock oder eine andere Bibliothek benutzen, um Mocks zu machen.
Sie können eventuell versuchen, automatisierte Refactoring-Tools wie Jackpot 3 oder RefactoringNG . Ich habe sie nie getestet, aber sie sind mehr als fähig zu tun, was Sie wollen, wenn Sie den Weg des Refactorings gehen.
Ein anderer Weg wäre die Verwendung von Powermock, was es erlaubt, Finals und Statik zu spielen (selbst wenn ich nicht gerne statische Zustände verspotze, da es etwas in deinem Design nicht stimmt).
Sie können immer Delegierung statt Vererbung verwenden.
%Vor%Es ist jedoch eine schlechte Idee, abstrakte Klassen in Ihrer API zu haben. Interface wird besser sein.
IMO, Klassen werden als endgültig markiert, um ihre Objekte unveränderlich zu machen. Wenn Sie das Verhalten einer Klasse steuern möchten, markieren Sie die Methoden in der Klasse als privat, sodass sie nicht überschrieben werden können. Wenn Sie sie geschützt lassen, kann jemand Ihre Klasse erweitern, diese geschützten Methoden überschreiben und eine Instanz der erweiterten Klasse an Ihre APIs übergeben, die Ihr Klassenobjekt als Parameter erwarten, wodurch das geänderte Verhalten ausgelöst wird. Also würden Sie Methoden markieren, die Sie nicht als privat ausliefern möchten.
Sie hinterlassen nur Erweiterungspunkte in Bezug auf protected / public-Methoden, die von den Clients, die diese Klassen verwenden, für benutzerdefiniertes Verhalten außer Kraft gesetzt werden können.
Die meisten Klassen final
sind eine gute Übung, da sie normalerweise nicht für die Erweiterung durch Unterklassen ausgelegt sind. Alle Bücher über API-Design, die ich kenne, empfehlen, dies zu tun ("Effektives Java", "Praktisches API-Design", "API-Design für C ++"). Dies ist aus mehreren Gründen vorteilhaft, einschließlich des Ausdrucks der Absicht des API-Designers, der sicheren Entwicklung der API und der Verhinderung von toten Code (z. B. erkennt eine gute IDE, wenn eine final
-Methode keine der Methoden verwendet seine Parameter oder niemals eine in der throws
-Klausel aufgelistete geprüfte Ausnahme - was für eine nicht-finale Methode nicht möglich wäre).
Um diese Klassen zu verspotten, müssen Sie nur ein richtiges Mock-Tool wie JMockit verwenden (das ich genau entwickelt habe) weil ich Komponententests schreiben wollte, ohne bestimmte OO / API-Design-Praktiken zu opfern).
Tags und Links java maven unit-testing mocking mockito