Späte Antwort, aber ich kann nicht widerstehen.
Ist X die meisten Klassen in Y gut oder ein Anti-Muster?
In den meisten Fällen werden die meisten Regeln, die ohne nachzudenken angewendet werden, schrecklich schief gehen (einschließlich dieser).
Lassen Sie mich Ihnen eine Geschichte über die Geburt eines Objekts im Chaos eines richtigen, schnellen und schmutzigen Verfahrenscodes erzählen, der nicht beabsichtigt, sondern aus Verzweiflung entstanden ist.
Mein Praktikant und ich programmieren Paare, um schnell einen Wegwerfcode zum Abkratzen einer Webseite zu erstellen. Wir haben absolut keinen Grund zu der Annahme, dass dieser Code lange leben wird, also schlagen wir nur etwas heraus, das funktioniert. Wir nehmen die ganze Seite als Schnur und hacken das Zeug, das wir brauchen, auf die erstaunlich spröde Art und Weise, die Sie sich vorstellen können. Beurteile nicht. Es klappt.
Währenddessen habe ich einige statische Methoden zum Hacken erstellt. Mein Praktikant hat eine DTO-Klasse erstellt, die Ihrer sehr ähnlich war CatData
.
Als ich mir das DTO zum ersten Mal ansah, nervte es mich. Die jahrelangen Schäden, die Java meinem Gehirn zugefügt hat, haben mich auf den öffentlichen Feldern zurückgeworfen. Aber wir haben in C # gearbeitet. C # benötigt keine vorzeitigen Getter und Setter, um Ihr Recht zu bewahren, die Daten unveränderlich zu machen oder später zu kapseln. Ohne die Benutzeroberfläche zu ändern, können Sie sie jederzeit hinzufügen. Vielleicht nur, damit Sie einen Haltepunkt setzen können. Alles ohne Ihren Kunden etwas darüber zu erzählen. Ja C #. Boo Java.
Also hielt ich meine Zunge. Ich sah zu, wie er meine statischen Methoden verwendete, um dieses Ding zu initialisieren, bevor er es benutzte. Wir hatten ungefähr 14 davon. Es war hässlich, aber wir hatten keinen Grund, uns darum zu kümmern.
Dann brauchten wir es an anderen Orten. Wir wollten den Code kopieren und einfügen. 14 Initialisierungszeilen werden herumgeschleudert. Es begann schmerzhaft zu werden. Er zögerte und fragte mich nach Ideen.
Widerwillig fragte ich: "Würden Sie ein Objekt in Betracht ziehen?"
Er schaute zurück zu seinem DTO und verzog verwirrt das Gesicht. "Es ist ein Objekt".
"Ich meine ein echtes Objekt"
"Huh?"
"Lass mich dir etwas zeigen. Du entscheidest, ob es nützlich ist."
Ich wählte einen neuen Namen und peitschte schnell etwas auf, das ein bisschen so aussah:
public class Cat{
CatData(string catPage) {
this.catPage = catPage
}
private readonly string catPage;
public string name() { return chop("name prefix", "name suffix"); }
public string weight() { return chop("weight prefix", "weight suffix"); }
public string image() { return chop("image prefix", "image suffix"); }
private string chop(string prefix, string suffix) {
int start = catPage.indexOf(prefix) + prefix.Length;
int end = catPage.indexOf(suffix);
int length = end - start;
return catPage.Substring(start, length);
}
}
Dies hat nichts bewirkt, was die statischen Methoden noch nicht getan haben. Aber jetzt hatte ich die 14 statischen Methoden in eine Klasse gesaugt, in der sie mit den Daten, an denen sie arbeiteten, allein sein konnten.
Ich habe meinen Praktikanten nicht gezwungen, es zu benutzen. Ich bot es einfach an und ließ ihn entscheiden, ob er sich an die statischen Methoden halten wollte. Ich ging nach Hause und dachte, er würde sich wahrscheinlich an das halten, was er bereits hatte. Am nächsten Tag stellte ich fest, dass er es an vielen Orten benutzte. Es löschte den Rest des Codes, der immer noch hässlich und prozedural war, aber diese Komplexität war uns jetzt hinter einem Objekt verborgen. Es war ein bisschen besser.
Jetzt ist sicher, dass jedes Mal, wenn Sie darauf zugreifen, einiges an Arbeit geleistet wird. Ein DTO ist ein schöner, schnell zwischengespeicherter Wert. Ich machte mir darüber Sorgen, erkannte jedoch, dass ich das Caching hinzufügen konnte, wenn wir es jemals brauchten, ohne den verwendeten Code zu berühren. Also werde ich mich nicht darum kümmern, bis es uns wichtig ist.
Soll ich damit sagen, dass Sie sich immer an OO-Objekte über DTOs halten sollten? Nein. DTOs leuchten, wenn Sie eine Grenze überschreiten müssen, die Sie davon abhält, Methoden zu verschieben. DTOs haben ihren Platz.
Aber auch OO-Objekte. Erfahren Sie, wie Sie beide Tools verwenden. Erfahren Sie, was jeder kostet. Lernen Sie, das Problem, die Situation und den Praktikanten entscheiden zu lassen. Dogma ist hier nicht dein Freund.
Da meine Antwort bereits lächerlich lang ist, möchte ich Sie bei einer Überprüfung Ihres Codes von einigen Missverständnissen abbringen.
Beispielsweise hat eine Klasse normalerweise Klassenmitglieder und -methoden, z.
public class Cat{
private String name;
private int weight;
private Image image;
public void printInfo(){
System.out.println("Name:"+this.name+",weight:"+this.weight);
}
public void draw(){
//some draw code which uses this.image
}
}
Wo ist dein Konstruktor? Dies zeigt mir nicht genug, um zu wissen, ob es nützlich ist.
Aber nachdem ich über das Prinzip der Einzelverantwortung und das Prinzip der offenen Schließung gelesen habe, ziehe ich es vor, eine Klasse nur mit statischen Methoden in DTO- und Hilfsklasse zu unterteilen, z.
public class CatData{
public String name;
public int weight;
public Image image;
}
public class CatMethods{
public static void printInfo(Cat cat){
System.out.println("Name:"+cat.name+",weight:"+cat.weight);
}
public static void draw(Cat cat){
//some draw code which uses cat.image
}
}
Ich denke, es passt zum Prinzip der Einzelverantwortung, da CatData jetzt nur noch Daten aufbewahrt und sich nicht um die Methoden kümmert (auch nicht um CatMethods).
Sie können im Namen des Einzelverantwortungsprinzips viele dumme Dinge tun. Ich könnte argumentieren, dass Cat Strings und Cat Ints getrennt werden sollten. Diese Zeichenmethoden und Bilder müssen alle ihre eigene Klasse haben. Dass Ihr laufendes Programm eine einzige Verantwortung ist, sollten Sie nur eine Klasse haben. : P.
Für mich besteht der beste Weg, dem Prinzip der Einzelverantwortung zu folgen, darin, eine gute Abstraktion zu finden, mit der Sie Komplexität in eine Box packen und sie verbergen können. Wenn Sie ihm einen guten Namen geben können, der die Leute davon abhält, überrascht zu werden, was sie finden, wenn sie hineinschauen, haben Sie ihn ziemlich gut verfolgt. Zu erwarten, dass es mehr Entscheidungen diktiert, als es Ärger bereitet. Ehrlich gesagt, beide Code-Listen tun dies, sodass ich nicht verstehe, warum SRP hier wichtig ist.
Und es passt auch zum Open-Closed-Prinzip, da durch Hinzufügen neuer Methoden die CatData-Klasse nicht geändert werden muss.
Nun nein. Beim Open-Close-Prinzip geht es nicht darum, neue Methoden hinzuzufügen. Es geht darum, die Implementierung alter Methoden ändern zu können und nichts bearbeiten zu müssen. Nichts, was dich benutzt und nicht deine alten Methoden. Stattdessen schreiben Sie irgendwo anders neuen Code. Eine Form von Polymorphismus wird das gut machen. Sehen Sie das hier nicht.
Meine Frage ist, ist es ein gutes oder ein Anti-Muster?
Zum Teufel, woher soll ich das wissen? Schauen Sie, es hat Vorteile und Kosten. Wenn Sie Code von Daten trennen, können Sie beide ändern, ohne den anderen neu kompilieren zu müssen. Vielleicht ist Ihnen das von entscheidender Bedeutung. Vielleicht macht es Ihren Code nur sinnlos kompliziert.
Wenn Sie sich dadurch besser fühlen, sind Sie nicht weit von etwas entfernt, das Martin Fowler als Parameterobjekt bezeichnet . Sie müssen nicht nur Grundelemente in Ihr Objekt aufnehmen.
Ich möchte, dass Sie ein Gespür dafür entwickeln, wie Sie Ihre Trennung in beiden Codierungsstilen durchführen oder nicht. Ob Sie es glauben oder nicht, Sie werden nicht gezwungen, einen Stil zu wählen. Sie müssen nur mit Ihrer Wahl leben.