OO Design Beratung – toString

Also habe ich die Address class bekommen:

 class Address { private String streetAddress; private int number; private String postalCode; private City city; private State state; private Country country; } 

Und ich möchte seine lesbare Version beispielsweise in einer Rasterspalte anzeigen.

Was ist der beste und prägnanteste Weg, dies zu implementieren?

  1. toString Methode innerhalb der class Address ( Ich persönlich mag diesen Ansatz nicht, da ‘toString’ nicht direkt mit einer Adresse verknüpft ist )
  2. class ReadableAddressFormatter
    • ReadableAddressFormatter ( Address addressToFormat )
    • öffentliche String getFormatted()
  3. Vorherige class aber getFormmated würde statisch sein, die Address Instanz empfangen und die Zeichenfolge zurückgeben
  4. Andere? Vorschläge bitte.

Ich suche ein gutes Design und konzentriere mich auch auf Clean Code , Entkopplung und Wartbarkeit .

Alle diese Methoden wurden verwendet, und es gibt keine Möglichkeit, eine “kontextunabhängige” Best Practice anzubieten. Die beste Antwort in Software Engineering ist normalerweise “es kommt darauf an.” Das heißt, lassen Sie uns jedes analysieren:

  1. Der KISS-Ansatz vom Feinsten. Ich tue dies für alle meine grundlegenden “Print to Console, stellen Sie sicher, dass die Dinge funktionieren” Art der Sache. Wenn Sie ein spezifisches Format haben, das Sie für Adressen erwarten können, ist dies die niedrig hängende Frucht- / Easy-Win-Lösung. Sie können dies jederzeit überschreiben oder das Objekt in einmaligen Situationen anders ausdrucken.
  2. Dies ist die erweiterbarste Lösung, da sie eine Lokalisierung und benutzerdefinierte Formatierung ermöglicht. Ob es angemessen ist, hängt davon ab, wie oft Sie erwarten, dass Adressen in verschiedenen Formaten angezeigt werden. Benötigst du wirklich diesen Todesstern, um eine Fliege auszuschalten, oder kannst du in Großbuchstaben wechseln oder zwischen den für deine App wichtigen Sprachen wechseln?
  3. Ich würde diesen Ansatz nicht vorschlagen, da es im Allgemeinen anfing, die “Ansichtsebene” -Logik in die Domäne zu bluten, was normalerweise am besten von anderen Ebenen gehandhabt wird (in einem classn-MVC-Ansatz). Man könnte argumentieren, dass toString () das Gleiche macht, aber toString () auch als “Name” oder “Essenz” dafür gedacht werden kann, wie ein Objekt der Außenwelt erscheint, also würde ich sagen, es ist mehr als nur präsentativ .

Ich hoffe, dass dies hilft, und ein großes Lob für das Denken über Clean Code, Entkopplung und Wartbarkeit von Anfang an.

Für ein Beispiel des Prinzips # 2 in Aktion – unter Verwendung des Strategie-Pattern , der Einhaltung des Prinzips der einfachen Verantwortlichkeit , des Open / Closed-Prinzips und der Inversion der Kontrolle über Dependency Injection – vergleiche den folgenden Ansatz (freundlicherweise zur Verfügung gestellt von @SteveJ) :

 public class Address { private String streetAddress; private int number; private String postalCode; private String city; private String state; private String country; public String toLongFormat(){ return null; // stitch together your long format } public String toShortFormat(){ return null; // stitch together your short format } public String toMailingLabelFormat(){ return null; // stitch together your mailing label format } @Override public String toString(){ return toShortFormat(); // your default format } } } 

Mit diesem (in “meistens korrekt” Groovy):

 public interface AddressFormatter { String format(Address toFormat) } public class LongAddressFormatter implements AddressFormatter { @Override public String format(Address toFormat){ return String.format("%sBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAH%n%s", toFormat.streetAddress, toFormat.postalCode) } } public class ShortAddressFormatter implements AddressFormatter { @Override public String format(Address toFormat){ return String.format("%d", toFormat.number) } } public class Address { private String streetAddress; private int number; private String postalCode; private String city; private String state; private String country; public AddressFormatter formatter = new ShortAddressFormatter(); // just to avoid NPE public void setFormatter(AddressFormatter fr) { this.formatter = fr; } @Override public String toString(){ return formatter.format(this); // your default format } } def addrr = new Address(streetAddress:"1234 fun drive", postalCode:"11223", number:1) addr.setFormatter(new LongAddressFormatter()); println "The address is ${addrr}" addr.setFormatter(new ShortAddressFormatter()); println "The address is ${addrr}" 

Wie @steveJ festgestellt hat:

“So haben Sie unterschiedliche Formatierungsstrategien” und Sie können zwischen ihnen wechseln … Ich hatte die Idee, dass Sie die Formatierung einmal einstellen und dabei bleiben würden … UND wenn Sie einen anderen Formatierungsstil hinzufügen wollen, dann tun Sie das nicht Ich muss die Adressklasse nicht öffnen und neu schreiben, sondern einen neuen separaten Stil schreiben und ihn einfügen, wenn Sie ihn verwenden wollen. ”

.NET LÖSUNG:

Das Überschreiben von Object.ToString() scheint die logischste Lösung zu sein. Dies macht es sauber in Situationen wie: Console.WriteLine("Home Address: {0}", homeAddress);

Wenn Sie zusätzliche Formatierung bereitstellen möchten, sollte die class Address IFormattable implementieren.

Außerdem sollten Sie eine AddressFormatter-class erstellen, die IFormatProvider und ICustomFormatter .

Die MSDN-Links bieten sehr gut Beispiele (ein BinaryFormatter und ein AcctNumberFormat), aber wenn diese nicht genug sind, schauen Sie sich auch dieses gute Beispiel an: PhoneFormatter


Wenn Sie sich dazu entschließen, dies vollständig zu tun und IFormattable und einen benutzerdefinierten IFormatProvider / ICustomFormatter zu implementieren, würde ich vorschlagen, dass Sie ToString () einfach mit einem Standardanbieter zu Ihrem ToString (Stringformat, IFormatProvider formatProvider) aufrufen. Auf diese Weise können Sie Dinge wie Lokalisierung und Arten von Adressen (kurz, lang, usw.) berücksichtigen.

toString benötigt kein zusätzliches Gepäck außerhalb der function selbst; scheint die einfachste Lösung zu sein. Es ist aus einem bestimmten Grund da, oder?

Normalerweise unterteile ich die Präsentationsschicht von der Datenschicht. Die Darstellung in einer GUI scheint mir etwas mit der Darstellungsschicht und nicht mit der Datenschicht zu tun zu haben.

Ich würde vorschlagen, dass Sie eine function irgendwo in Ihrer Präsentationsschicht setzen, die die Adresse in Zeichenkette umwandelt.

Präsentation der Daten ist nicht mit Daten verbunden!

Eine statische Methode ist gut. Eine Konverterklasse wäre besser, Sie können eine einzige Instanz für Ihre Anwendung behalten, aber Sie können sie ersetzen oder eine andere schreiben, wenn Sie Ihre Anwendung von GUI zu WEB mit einem anderen Format bewegen oder wenn Sie in einem Fenster alles und in anzeigen möchten ein anderes Fenster, in dem Sie nur einen Teil der Informationen oder Informationen anzeigen möchten, die auf andere Weise formatiert sind.

Es gibt mehrere Modelle, denen Sie folgen können, zum Beispiel verwendet Microsoft WPF einen völlig anderen Ansatz, den MVVM, Model View View Model, der es Ihnen erlaubt, die Datenschicht sehr gut von der Geschäftslogik von der Präsentationsebene zu trennen.

Normalerweise überschreibe ich ToString in C # oder toString in Java nur zu Debuggingzwecken (indem ich eine Zeichenfolge präsentiere, die ich zum Debuggen verwenden kann) oder für eine Art einfacher Serialisierung, um einen String zu erstellen, normalerweise auch einen FromString (oder fromString-Methode in Java). Ein Beispiel sind benutzerdefinierte Typen wie Punkt, Vektor, Matrix und so weiter.

Über C # -Welt sprechen ..

 public class AddressToStringConverter { public virtual string ToString(Address address) { return address.Street + ", " + address.City } } 

Dann in Ihrer Form (zum Beispiel).

 AddressToStringConverter myConverter = new AddressToStringConverter(); public Address CurrentSelectedAddress { get { ... } } public button1_click(object sender, EventArgs e) { button1.Text = myConverter.Convert(address); } 

Wenn Sie möchten, können Sie andere nützliche Schnittstellen wie zum Beispiel ITypeConverter erstellen

toString () ist am flexibelsten und bequemsten und wird implizit aufgerufen, wenn Sie ein Objekt der Address-class mit einem String kombinieren, wie in System.out.println (“Meine Adresse ist” + objectOfAddressClass).

Der einzige Grund, warum ich daran denke, toString () nicht zu überschreiben, ist, wenn Sie die Formatierung ändern müssen. Dann würden Sie andere Methoden benötigen (wie in toMailingString () und toShortFormString () usw.) oder eine parametrisierte Methode (wie in toMailingString (boolean useShortForm) oder was auch immer), aber toString () wird es nicht schneiden.

Natürlich können (und sollten) Sie beides tun. Verwenden Sie toString () als Standard, rufen Sie wahrscheinlich eine Ihrer spezifischen Formatmethoden auf und verwenden Sie dann Ihre anderen Hilfsmethoden für alternative Formate.

 public class TestClass { class City{}; class State{}; class Country{}; class Address { private String streetAddress; private int number; private String postalCode; private City city; private State state; private Country country; public String toLongFormat(){ return null; // stitch together your long format } public String toShortFormat(){ return null; // stitch together your short format } public String toMailingLabelFormat(){ return null; // stitch together your mailing label format } @Override public String toString(){ return toShortFormat(); // your default format } } } 

Du hast deinen Post mit Java getaggt, also werde ich für Java (und Swing, genauer gesagt) antworten. Diese Aufgabe ist normalerweise Aufgabe eines bestimmten TableCellRenderer. Wenn dasselbe Format für andere visuelle Komponenten verwendet werden muss, würde ich tatsächlich die Formatierung innerhalb einer instanziierbaren class extrahieren (Lösung 2). Dadurch können Unterklassen das Format bei Bedarf anpassen.

Ich denke, dass eine toString() -Methode, die eine Zeichenfolge zurückgibt, der beste Ansatz ist. Wenn Sie eine Address-Instanz haben, sagen wir address , dann ist es offensichtlich, was address.toString() tut. Die Tatsache, dass toString() nicht direkt mit Address verknüpft ist, ändert nichts wirklich.