Angenommen, ich habe ein benutzerdefiniertes Objekt, Student :
public class Student{
public int _id;
public String name;
public int age;
public float score;
}
Und eine Klasse, Window , mit der Informationen eines Schülers angezeigt werden :
public class Window{
public void showInfo(Student student);
}
Es sieht ganz normal aus, aber ich fand, dass Window nicht ganz einfach einzeln zu testen ist, da es ein echtes Student- Objekt benötigt, um die Funktion aufzurufen. Deshalb versuche ich, showInfo so zu ändern, dass es ein Student- Objekt nicht direkt akzeptiert :
public void showInfo(int _id, String name, int age, float score);
Damit es einfacher ist, Window einzeln zu testen :
showInfo(123, "abc", 45, 6.7);
Aber ich fand die geänderte Version hat noch andere Probleme:
Modify Student (z. B. Hinzufügen neuer Eigenschaften) erfordert das Ändern der Methodensignatur von showInfo
Wenn Student viele Eigenschaften hätte, wäre die Methodensignatur von Student sehr lang.
Wenn Sie also benutzerdefinierte Objekte als Parameter verwenden oder jede Eigenschaft in Objekten als Parameter akzeptieren, welche ist besser zu warten?
showInfo
erfordert einen echten String, einen echten Float und zwei echte Ints. Wie ist esString
besser , ein reales Objekt bereitzustellen als ein realesStudent
Objekt?int
Parameter. Auf der Anrufseite gibt es keine Bestätigung, dass Sie sie tatsächlich in der richtigen Reihenfolge übergeben. Was ist, wenn Sieid
undage
, oderfirstName
und tauschenlastName
? Sie führen eine potenzielle Fehlerquelle ein, die sehr schwer zu erkennen ist, bis sie in Ihrem Gesicht explodiert, und Sie fügen sie an jedem Anrufstandort hinzu .showForm(bool, bool, bool, bool, int)
Methode - ich liebe diese ...Antworten:
Die Verwendung eines benutzerdefinierten Objekts zum Gruppieren verwandter Parameter ist eigentlich ein empfohlenes Muster. Als Refactoring wird es als Introduce Parameter Object bezeichnet .
Dein Problem liegt woanders. Erstens sollte generic
Window
nichts über Student wissen. Stattdessen sollten Sie eine Art vonStudentWindow
Wissen haben , das nur das Anzeigen betrifftStudents
. Zweitens ist es absolut kein Problem, eineStudent
zu testende Instanz zu erstellen ,StudentWindow
solangeStudent
sie keine komplexe Logik enthält, die das Testen von drastisch erschwertStudentWindow
. Wenn es diese Logik hat,Student
sollte es bevorzugt werden , eine Schnittstelle zu erstellen und sie zu verspotten.quelle
Student
Gruppierung ist sinnvoll und wird wahrscheinlich in anderen Bereichen der App auftreten.Student
, handelt es sich um ein Objekt, dasa.b.c
wenn Ihre Methode es erforderta
. Wenn Ihre Methode an den Punkt gelangt, an dem Sie ungefähr mehr als 4 Parameter oder 2 Ebenen des Eigentumserwerbs benötigen, muss sie wahrscheinlich herausgerechnet werden. Beachten Sie auch, dass dies eine Richtlinie ist - wie alle anderen Richtlinien auch, erfordert es die Diskretion des Benutzers. Folge ihm nicht blindlings.Du sagst es ist
Sie können jedoch auch ein Schülerobjekt erstellen, das an Ihr Fenster übergeben wird:
Es scheint nicht viel komplexer zu sein.
quelle
Student
auf a Bezug genommen wirdUniversity
, das auf vieleFaculty
s undCampus
s verweist , wobeiProfessor
s undBuilding
s keines von ihnenshowInfo
tatsächlich verwendet, Sie jedoch keine Schnittstelle definiert haben, mit der die Tests dies "wissen" und nur den relevanten Schüler beliefern können Daten, ohne die gesamte Organisation aufzubauen. Das BeispielStudent
ist ein einfaches Datenobjekt, und wie Sie sagen, sollten Tests gerne damit arbeiten.In den Begriffen des Laien:
Bearbeiten:
Wie @ Tom.Bowen89 feststellt , ist es nicht viel komplexer, die showInfo-Methode zu testen:
quelle
quelle
Steve McConnell ging in Code Complete genau auf dieses Problem ein und diskutierte die Vor- und Nachteile der Übergabe von Objekten an Methoden anstelle der Verwendung von Eigenschaften.
Verzeihen Sie mir, wenn ich einige Details falsch sehe. Ich arbeite aus dem Gedächtnis, da ich seit über einem Jahr keinen Zugriff mehr auf das Buch hatte:
Er kommt zu dem Schluss, dass Sie besser dran sind, kein Objekt zu verwenden, sondern nur die Eigenschaften zu senden, die für die Methode unbedingt erforderlich sind. Die Methode sollte außerhalb der Eigenschaften, die sie als Teil ihrer Operationen verwendet, nichts über das Objekt wissen müssen. Sollte sich das Objekt im Laufe der Zeit ändern, kann dies unbeabsichtigte Folgen für die Methode haben, die das Objekt verwendet.
Er sprach auch an, dass, wenn Sie eine Methode haben, die viele verschiedene Argumente akzeptiert, dies wahrscheinlich ein Zeichen dafür ist, dass die Methode zu viel tut und in mehrere, kleinere Methoden unterteilt werden sollte.
Manchmal, manchmal brauchen Sie jedoch tatsächlich viele Parameter. Das Beispiel, das er gibt, wäre eine Methode, die eine vollständige Adresse unter Verwendung vieler verschiedener Adresseigenschaften erstellt (obwohl dies durch die Verwendung eines String-Arrays umgangen werden könnte, wenn Sie darüber nachdenken).
quelle
Student
in diesem Fall von). Auf diese Weise informiert das Testen das Design und berücksichtigt die Antworten mit den meisten Stimmen bei gleichzeitiger Wahrung der Designintegrität.Es ist viel einfacher, Tests zu schreiben und zu lesen, wenn Sie das gesamte Objekt bestehen:
Zum Vergleich,
Zeile könnte geschrieben werden, wenn Sie Werte separat übergeben, als:
wo der eigentliche Methodenaufruf irgendwo wie begraben ist
Um auf den Punkt zu kommen, dass Sie den tatsächlichen Methodenaufruf nicht in den Test stellen können, ist dies ein Zeichen dafür, dass Ihre API fehlerhaft ist.
quelle
Sie sollten, was Sinn macht, einige Ideen weitergeben:
Einfacher zu testen. Was erfordert das geringste Refactoring, wenn die Objekte bearbeitet werden müssen? Ist die Wiederverwendung dieser Funktion für andere Zwecke nützlich? Was ist die geringste Menge an Informationen, die ich benötige, um dieser Funktion ihren Zweck zu geben? (Wenn Sie den Code auflösen, können Sie ihn möglicherweise wiederverwenden. Seien Sie also vorsichtig, wenn Sie die Entwurfslücke für die Ausführung dieser Funktion schließen und dann den Flaschenhals für die ausschließliche Verwendung dieses Objekts ausfüllen.)
Alle diese Programmierregeln sind nur Richtlinien, die Sie zum richtigen Denken anregen. Bauen Sie einfach kein Code-Biest - wenn Sie sich nicht sicher sind und nur fortfahren müssen, wählen Sie eine Richtung / Ihre eigene oder einen Vorschlag hier aus, und wenn Sie einen Punkt erreicht haben, an dem Sie denken: "Oh, ich hätte das schaffen sollen." way '- Sie können es dann wahrscheinlich ganz einfach umgestalten. (Wenn Sie beispielsweise eine Lehrerklasse haben, muss nur die gleiche Eigenschaft wie für Schüler festgelegt werden, und Sie ändern Ihre Funktion, um alle Objekte des Personenformulars zu akzeptieren.)
Am ehesten würde ich das übergebene Hauptobjekt beibehalten, da durch die Codierung leichter erklärt werden kann, was diese Funktion bewirkt.
quelle
Eine gängige Vorgehensweise ist das Einfügen einer Schnittstelle zwischen den beiden Prozessen.
Dies wird manchmal etwas chaotisch, aber in Java wird es ein wenig aufgeräumter, wenn Sie eine innere Klasse verwenden.
Sie können die
Window
Klasse dann testen, indem Sie ihr ein falschesHasInfo
Objekt geben.Ich vermute, dies ist ein Beispiel für das Decorator-Pattern .
Hinzugefügt
Die Einfachheit des Codes scheint Verwirrung zu stiften. Hier ist ein weiteres Beispiel, das die Technik besser demonstrieren könnte.
quelle
Student
undString
hier für den Rückgabetyp dient nur zur Demonstration. Es würde wahrscheinlich zusätzliche Parameter geben, aufgetInfo
diePane
gezeichnet werden kann, wenn gezeichnet wird. Hier geht es darum , funktionale Komponenten als Dekoratoren des ursprünglichen Objekts zu übergeben .HasInfo
Objekte.Student
weiß, wie man eins ist.getInfo
return void passPane
zum Zeichnen geben, wird die Implementierung (in derStudent
Klasse) plötzlich an swing oder was auch immer gekoppelt, das Sie verwenden. Wenn Sie dafür sorgen, dass eine Zeichenfolge zurückgegeben wird und 0-Parameter verwendet werden, weiß Ihre Benutzeroberfläche nicht, was mit der Zeichenfolge ohne magische Annahmen und implizite Kopplung zu tun ist. Wenn SiegetInfo
tatsächlich ein Ansichtsmodell mit relevanten Eigenschaften zurückgeben, ist IhreStudent
Klasse wieder an die Präsentationslogik gekoppelt. Ich halte keine dieser Alternativen für wünschenswertSie haben bereits viele gute Antworten, aber hier sind noch ein paar Vorschläge, mit denen Sie eine alternative Lösung finden können:
In Ihrem Beispiel wird ein Student (eindeutig ein Modellobjekt) an ein Window (anscheinend ein Objekt auf Ansichtsebene) übergeben. Ein zwischengeschaltetes Controller- oder Presenter-Objekt kann hilfreich sein, wenn Sie noch keines haben, sodass Sie Ihre Benutzeroberfläche von Ihrem Modell isolieren können. Der Controller / Presenter sollte eine Schnittstelle bereitstellen, die zum Ersetzen für das Testen der Benutzeroberfläche verwendet werden kann, und Schnittstellen verwenden, um auf Modellobjekte zu verweisen und Objekte anzuzeigen, um sie von beiden zu Testzwecken isolieren zu können. Möglicherweise müssen Sie eine abstrakte Methode zum Erstellen oder Laden dieser Objekte angeben (z. B. Factory-Objekte, Repository-Objekte oder ähnliches).
Das Übertragen relevanter Teile Ihrer Modellobjekte in ein Datenübertragungsobjekt ist ein nützlicher Ansatz für die Schnittstelle, wenn Ihr Modell zu komplex wird.
Möglicherweise verstößt Ihr Schüler gegen das Prinzip der Schnittstellentrennung. In diesem Fall kann es hilfreich sein, die Oberfläche in mehrere Benutzeroberflächen zu unterteilen, mit denen einfacher zu arbeiten ist.
Lazy Loading kann das Erstellen großer Objektdiagramme vereinfachen.
quelle
Das ist eigentlich eine anständige Frage. Das eigentliche Problem hierbei ist die Verwendung des Oberbegriffs "Objekt", der etwas mehrdeutig sein kann.
Im Allgemeinen bedeutet der Begriff "Objekt" in einer klassischen OOP-Sprache "Klasseninstanz". Klasseninstanzen können ziemlich umfangreich sein - öffentliche und private Eigenschaften (und solche dazwischen), Methoden, Vererbung, Abhängigkeiten usw. Sie möchten so etwas nicht wirklich verwenden, um einfach einige Eigenschaften zu übergeben.
In diesem Fall verwenden Sie ein Objekt als Container, der lediglich einige Grundelemente enthält. In C ++ wurden Objekte wie diese als
structs
(und sie existieren immer noch in Sprachen wie C #) bezeichnet. Strukturen wurden in der Tat genau für die Verwendung entworfen, von der Sie sprechen - sie gruppierten verwandte Objekte und Grundelemente, wenn sie eine logische Beziehung hatten.In modernen Sprachen gibt es jedoch keinen Unterschied zwischen einer Struktur und einer Klasse, wenn Sie den Code schreiben. Sie können also ein Objekt verwenden. (Hinter den Kulissen gibt es jedoch einige Unterschiede, die Sie beachten sollten - beispielsweise ist eine Struktur ein Werttyp und kein Referenztyp.) Grundsätzlich ist es einfach, solange Sie Ihr Objekt einfach halten manuell testen. Moderne Sprachen und Tools ermöglichen es Ihnen jedoch, dies erheblich zu verringern (über Schnittstellen, spöttische Frameworks, Abhängigkeitsinjektion usw.).
quelle
Student
) in Ansichtsmodelle (StudentInfo
oderStudentInfoViewModel
ähnliches) übersetzt, dies ist jedoch möglicherweise nicht erforderlich.