Ich bin auf einen Blog-Eintrag gestoßen, der die Verwendung von Strings in Java für das Fehlen von Semantik in Ihrem Code abschreckt, und habe vorgeschlagen, stattdessen Thin-Wrapper-Klassen zu verwenden. Dies ist das Vorher-Nachher-Beispiel, das der genannte Eintrag zur Veranschaulichung der Angelegenheit enthält:
public void bookTicket(
String name,
String firstName,
String film,
int count,
String cinema);
public void bookTicket(
Name name,
FirstName firstName,
Film film,
Count count,
Cinema cinema);
In meiner Erfahrung mit dem Lesen von Programmierblogs bin ich zu dem Schluss gekommen, dass 90% Unsinn sind, aber ich frage mich, ob dies ein gültiger Punkt ist. Irgendwie fühlt es sich für mich nicht richtig an, aber ich konnte nicht genau herausfinden, was mit dem Programmierstil nicht stimmt.
Antworten:
Die Kapselung schützt Ihr Programm vor Änderungen . Wird sich die Darstellung eines Namens ändern? Wenn nicht, dann verschwenden Sie Ihre Zeit und YAGNI bewirbt sich.
Edit: Ich habe den Blogpost gelesen und er hat eine grundsätzlich gute Idee. Das Problem ist, dass er es viel zu weit skaliert hat. Sowas
String orderId
ist echt schlimm, weil es vermutlich"!"£$%^&*())ADAFVF
keine Gültigkeit hatorderId
. Dies bedeutet, dass esString
viel mehr mögliche Werte gibt als gültigeorderId
s. Für etwas wie aname
können Sie jedoch unmöglich vorhersagen, was ein gültiger Name sein könnte oder nicht, und anyString
ist ein gültigername
.In erster Linie reduzieren Sie die möglichen Eingaben (korrekt) auf nur gültige. In der zweiten Instanz haben Sie keine Einschränkung möglicher gültiger Eingaben erreicht.
Erneut bearbeiten: Betrachten Sie den Fall einer ungültigen Eingabe. Wenn Sie "Gareth Gobulcoque" als Ihren Namen schreiben, wird es albern aussehen, es wird nicht das Ende der Welt sein. Wenn Sie eine ungültige OrderID eingeben, funktioniert sie wahrscheinlich nicht.
quelle
Name
Sie nicht versehentlich einen anderen nicht verwandten String-Wert übergeben können , wenn Ihr Parameter vom Typ ist. Wo Sie ein erwartenName
,Name
kompiliert nur ein Wille, und die Zeichenfolge "Ich schulde Ihnen 5 $" kann nicht versehentlich akzeptiert werden. (Bitte beachten Sie, dass dies in keinem Zusammenhang mit einer Namensvalidierung steht!)Das ist nur verrückt :)
quelle
var p = new Point(x: 10, y:20);
kann. Es ist auch nicht so, als ob Cinema sich von einer Zeichenfolge unterscheidet. Ich würde verstehen, wenn man sich mit physikalischen Größen wie Druck, Temperatur, Energie befassen würde, bei denen Einheiten unterschiedlich sind und manche nicht negativ sein können. Der Autor des Blogs muss versuchen, funktionsfähig zu sein.Meistens stimme ich dem Autor zu. Wenn es ein für ein Feld spezifisches Verhalten gibt , z. B. die Validierung einer Bestell-ID, würde ich eine Klasse erstellen, die diesen Typ darstellt. Sein zweiter Punkt ist noch wichtiger: Wenn Sie eine Reihe von Feldern haben, die ein Konzept wie eine Adresse darstellen, dann erstellen Sie eine Klasse für dieses Konzept. Wenn Sie in Java programmieren, zahlen Sie einen hohen Preis für die statische Eingabe. Sie können genauso gut den Wert bekommen, den Sie können.
quelle
Tu es nicht. Es wird alles überkomplizieren und du wirst es nicht brauchen
... ist die Antwort, die ich vor 2 Jahren hier geschrieben hätte. Jetzt bin ich mir allerdings nicht so sicher; Tatsächlich habe ich in den letzten Monaten begonnen, alten Code in dieses Format zu migrieren, nicht weil ich nichts Besseres zu tun habe, sondern weil ich ihn wirklich brauchte, um neue Funktionen zu implementieren oder vorhandene zu ändern. Ich verstehe die automatischen Abneigungen, die andere hier sehen, aber ich denke, das ist etwas, das ernsthafte Gedanken verdient.
Leistungen
Der größte Vorteil ist die Möglichkeit , den Code zu ändern und zu erweitern . Wenn du benutzt
Anstatt nur ein paar Ganzzahlen zu übergeben - was leider bei vielen Schnittstellen der Fall ist -, wird es viel einfacher, später eine weitere Dimension hinzuzufügen. Oder ändern Sie den Typ in
double
. Wenn Sie späterList<Author> authors
oderList<Person> authors
stattdessen verwenden, istList<String> authors
es viel einfacher, dem, was ein Autor darstellt, weitere Informationen hinzuzufügen. Wenn ich es so aufschreibe, fühle ich mich so, als würde ich das Offensichtliche ausdrücken, aber in der Praxis habe ich mich schuldig gemacht, Strings so oft zu verwenden, besonders in Fällen, in denen es am Anfang nicht offensichtlich war, dann würde ich mehr brauchen als ein Faden.Ich versuche gerade, eine String-Liste umzugestalten, die in meinem Code verflochten ist, da ich dort weitere Informationen benötige, und ich fühle den Schmerz: \
Darüber hinaus stimme ich dem Autor des Blogs zu, dass es mehr semantische Informationen enthält , die dem Leser das Verständnis erleichtern. Während Parameter häufig mit aussagekräftigen Namen versehen werden und eine eigene Dokumentationszeile erhalten, ist dies bei Feldern oder Einheimischen häufig nicht der Fall.
Der letzte Vorteil ist die Sicherheit geben , aus offensichtlichen Gründen, aber es ist eine kleine Sache hier in meinen Augen.
Nachteile
Das Schreiben dauert länger . Das Schreiben einer kleinen Klasse ist schnell und einfach, aber nicht mühelos, insbesondere wenn Sie viele dieser Klassen benötigen. Wenn Sie alle 3 Minuten anhalten, um eine neue Wrapper-Klasse zu schreiben, kann dies auch Ihre Konzentration beeinträchtigen. Ich würde jedoch gerne glauben, dass dieser Zustand der Anstrengung normalerweise erst in der ersten Phase des Schreibens von Code auftritt. Normalerweise kann ich schnell eine ziemlich gute Vorstellung davon bekommen, welche Einheiten involviert sein müssen.
Es können viele redundante Setter (oder Konstruktionen) und Getter beteiligt sein . Der Blog-Autor gibt das wirklich hässliche Beispiel von
new Point(x(10), y(10))
anstattnew Point(10, 10)
, und ich möchte hinzufügen, dass eine Verwendung auch Dinge wieMath.max(p.x.get(), p.y.get())
statt beinhalten könnteMath.max(p.x, p.y)
. Und langer Code wird oft zu Recht als schwieriger zu lesen angesehen. Um ehrlich zu sein , habe ich das Gefühl, dass viel Code Objekte bewegt und nur mit ausgewählten Methoden erstellt wird, und noch weniger müssen auf die wichtigen Details zugreifen (was ohnehin nicht OOPy ist).Fraglich
Ich würde sagen, ob dies zur Lesbarkeit von Code beiträgt oder nicht, ist fraglich. Ja, mehr semantische Informationen, aber längerer Code. Ja, es ist einfacher, die Rolle eines jeden Orts zu verstehen, aber es ist schwieriger zu verstehen, was Sie damit machen können, wenn Sie nicht die Dokumentation lesen.
Wie bei den meisten anderen Programmierschulen ist es meines Erachtens ungesund, diese auf die Spitze zu treiben. Ich kann mir nicht vorstellen, dass ich die x- und y-Koordinate je nach Typ trenne. Ich denke nicht, dass
Count
es notwendig ist, wenn eineint
ausreicht. Ich mag dieunsigned int
Verwendung in C nicht - obwohl es theoretisch gut ist, gibt es Ihnen einfach nicht genug Informationen und es verbietet Ihnen, Ihren Code später zu erweitern, um dieses magische -1 zu unterstützen. Manchmal braucht man Einfachheit.Ich denke, dieser Blog-Beitrag ist etwas extrem. Aber insgesamt habe ich aus schmerzhaften Erfahrungen gelernt, dass die Grundidee dahinter aus den richtigen Dingen besteht.
Ich habe eine tiefe Abneigung gegen überentwickelten Code. Das tue ich wirklich. Aber richtig eingesetzt, finde ich diese Überentwicklung nicht.
quelle
Obwohl es eine Art Overkill ist, denke ich oft, dass die meisten Dinge, die ich gesehen habe, stattdessen unterentwickelt sind.
Es ist nicht nur "Sicherheit", eines der wirklich schönen Dinge an Java ist, dass es Ihnen sehr dabei hilft, sich genau zu merken / herauszufinden, was ein bestimmter Aufruf einer Bibliotheksmethode benötigt / erwartet.
Die mit Abstand schlechteste Java-Bibliothek, mit der ich gearbeitet habe, wurde von jemandem geschrieben, der Smalltalk sehr mochte und die GUI-Bibliothek so modellierte, dass sie eher Smalltalk ähnelt. Das Problem bestand darin, dass für jede Methode dasselbe Basisobjekt verwendet wurde. Sie konnten jedoch nicht wirklich alles NUTZEN, auf das das Basisobjekt umgewandelt werden konnte, sodass Sie immer wieder erraten mussten, was in Methoden übergegangen werden sollte, und nicht wussten, ob Sie bis zur Laufzeit einen Fehler hatten (etwas, mit dem ich mich jedes Mal befassen musste, wenn ich arbeitete in C).
Ein weiteres Problem - Wenn Sie Zeichenfolgen, Ints, Sammlungen und Arrays ohne Objekte weitergeben, haben Sie nur Datenbälle ohne Bedeutung. Dies erscheint natürlich, wenn Sie in Bezug auf Bibliotheken denken, dass "einige Apps" verwendet werden, aber beim Entwerfen einer gesamten App ist es viel hilfreicher, allen Ihren Daten an der Stelle, an der die Daten definiert sind, eine Bedeutung (Code) zuzuweisen und nur zu denken Begriffe, wie diese übergeordneten Objekte interagieren. Wenn Sie Primitive anstelle von Objekten weitergeben, ändern Sie per Definition Daten an einem anderen Ort als dem, an dem sie definiert sind. (Aus diesem Grund mag ich Setter und Getters nicht wirklich. Sie arbeiten also mit demselben Konzept auf Daten, die nicht Ihnen gehören).
Wenn Sie separate Objekte für alles definieren, haben Sie immer eine großartige Möglichkeit, alles zu überprüfen. Wenn Sie beispielsweise ein Objekt für die Postleitzahl erstellen und später feststellen, dass Sie sicherstellen müssen, dass die Postleitzahl immer die vierstellige Erweiterung enthält, die Sie haben perfekter Ort, um es auszudrücken.
Das ist eigentlich keine schlechte Idee. Wenn ich darüber nachdenke, bin ich mir nicht einmal sicher, ob ich sagen würde, dass es überhaupt überarbeitet wurde. Es ist in fast jeder Hinsicht einfacher, damit zu arbeiten. Die einzige Ausnahme ist die Verbreitung winziger Klassen, aber Java-Klassen sind so leicht und einfach zu schreiben, dass dies kaum Kosten verursacht (sie können sogar generiert werden).
Es würde mich sehr interessieren, ein gut geschriebenes Java-Projekt zu sehen, für das tatsächlich zu viele Klassen definiert wurden (wo dies das Programmieren erschwert hat). Ich fange an zu glauben, dass es nicht möglich ist, zu viele Klassen zu haben.
quelle
Ich denke, Sie müssen dieses Konzept von einem anderen Ausgangspunkt aus betrachten. Werfen Sie einen Blick aus der Perspektive eines Datenbankdesigners: Die im ersten Beispiel übergebenen Typen definieren Ihre Parameter nicht auf einzigartige Weise, geschweige denn auf nützliche Weise.
Es werden zwei Parameter benötigt, um den tatsächlichen Benutzer anzugeben, der Tickets bucht. Möglicherweise haben Sie zwei verschiedene Filme mit identischen Namen (z. B. Remakes). Möglicherweise haben Sie denselben Film mit unterschiedlichen Namen (z. B. Übersetzungen). Eine bestimmte Kette von Kinos hat möglicherweise unterschiedliche Zweigstellen. Wie gehen Sie also konsequent damit um (z. B. verwenden Sie
$chain ($city)
oder$chain in $city
oder sogar etwas anderes und wie stellen Sie sicher, dass dies der Fall ist ?) Das Schlimmste ist, dass Sie Ihren Benutzer mithilfe von zwei Parametern angeben. Die Tatsache, dass sowohl ein Vor- als auch ein Nachname angegeben werden, garantiert keinen gültigen Kunden (und Sie können zwei nicht unterscheidenJohn Doe
).Die Antwort darauf ist, Typen zu deklarieren, aber dies werden selten dünne Wrapper sein, wie ich oben zeige. Höchstwahrscheinlich fungieren sie als Datenspeicher oder sind mit einer Datenbank gekoppelt. Ein
Cinema
Objekt hat also wahrscheinlich einen Namen, einen Ort, ... und auf diese Weise werden solche Mehrdeutigkeiten beseitigt. Wenn sie dünne Hüllen sind, sind sie zufällig.Also, IMHO der Blog-Post sagt nur "Stellen Sie sicher, dass Sie die richtigen Typen übergeben", hat sein Autor gerade die übermäßig eingeschränkte Wahl getroffen, um insbesondere bei grundlegenden Datentypen auszuwählen (was die falsche Nachricht ist).
Die vorgeschlagene Alternative ist besser:
Andererseits denke ich, dass der Blog-Beitrag zu weit geht, wenn man alles einpackt.
Count
ist zu allgemein, ich könnte Äpfel oder Orangen damit zählen, sie hinzufügen und noch eine Situation in meinen Händen haben, in der das Typensystem mir erlaubt, unsinnige Operationen durchzuführen. Sie können natürlich die gleiche Logik wie im Blog anwenden und TypenCountOfOranges
usw. definieren , aber das ist auch einfach albern.Für das, was es wert ist, würde ich tatsächlich so etwas schreiben
Lange Rede kurzer Sinn: Sie sollten keine unsinnigen Variablen übergeben. Das einzige Mal, wenn Sie tatsächlich ein Objekt mit einem Wert angeben, der das tatsächliche Objekt nicht bestimmt, ist das Ausführen einer Abfrage (z. B.
public Collection<Film> findFilmsWithTitle(String title)
) oder das Erstellen eines Proof-of-Concept. Halten Sie Ihr Typensystem sauber, verwenden Sie also keinen zu allgemeinen (z. B. einen durch a dargestellten FilmString
) oder zu restriktiven / spezifischen / erfundenen (z . B.Count
stattint
) Typ . Verwenden Sie einen Typ, der Ihr Objekt eindeutig definiert, wann immer dies möglich und machbar ist.edit : eine noch kürzere zusammenfassung. Für kleine Anwendungen (zB Proof of Concepts): Warum kompliziertes Design? Einfach
String
oder benutzenint
und loslegen.Für große Anwendungen: Ist es wirklich so wahrscheinlich, dass Sie viele Klassen haben, die aus einem einzigen Feld mit einem Basisdatentyp bestehen? Wenn Sie wenig solche Klassen haben, haben Sie nur "normale" Objekte, nichts Besonderes ist dort los.
Ich bin der Meinung, dass die Idee, Zeichenfolgen zu kapseln, nur ein unvollständiges Design ist: für kleine Anwendungen zu kompliziert, für große Anwendungen nicht vollständig genug.
quelle
Für mich ist dies dasselbe wie die Verwendung von Regionen in C #. Wenn Sie der Meinung sind, dass dies erforderlich ist, um Ihren Code lesbar zu machen, haben Sie im Allgemeinen größere Probleme, mit denen Sie Ihre Zeit verbringen sollten.
quelle
Ich würde sagen, es ist eine wirklich gute Idee in einer Sprache mit einem stark typisierten typedef-ähnlichen Merkmal.
In Java gibt es das nicht. Wenn Sie also eine ganz neue Klasse für diese Dinge erstellen, überwiegen wahrscheinlich die Kosten. Sie können auch 80% des Nutzens erzielen, indem Sie vorsichtig mit der Benennung von Variablen / Parametern umgehen.
quelle
Es wäre gut, wenn der WENN-String (Ende Ganzzahl und ... nur String) nicht endgültig wäre. Diese Klassen könnten also ein (eingeschränkter) String mit Bedeutung sein und dennoch an ein unabhängiges Objekt gesendet werden, das weiß, wie man damit umgeht der Basistyp (ohne sich hin und her zu unterhalten).
Und "goodnes" davon nehmen zu, wenn es z. Einschränkungen für alle Namen.
Beim Erstellen einer App (nicht einer Bibliothek) ist es jedoch immer möglich, sie umzugestalten. Also fange ich lieber ohne an.
quelle
Ein Beispiel: In unserem derzeit entwickelten System gibt es viele verschiedene Entitäten, die durch mehrere Arten von Bezeichnern (aufgrund der Verwendung externer Systeme) identifiziert werden können, manchmal sogar dieselbe Art von Entität. Alle Bezeichner sind Strings. Wenn also jemand verwechselt, welche Art von ID als Parameter übergeben werden soll, wird kein Fehler bei der Kompilierung angezeigt, aber das Programm wird zur Laufzeit explodieren. Das passiert ziemlich oft. Daher muss ich sagen, dass der Hauptzweck dieses Prinzips nicht darin besteht, uns vor Veränderungen zu schützen (obwohl dies auch dazu dient), sondern uns vor Fehlern zu schützen. Und wenn jemand eine API entwirft, muss diese die Konzepte der Domäne widerspiegeln. Daher ist es konzeptionell nützlich, domänenspezifische Klassen zu definieren - alles hängt davon ab, ob die Entwickler die richtige Reihenfolge haben.
quelle