Prinzip der einheitlichen Verantwortung - Wie kann ich die Fragmentierung von Code vermeiden?

57

Ich arbeite in einem Team, in dem der Teamleiter ein virulenter Verfechter der SOLID-Entwicklungsprinzipien ist. Es fehlt ihm jedoch viel Erfahrung, um komplexe Software aus der Tür zu bekommen.

Wir haben eine Situation, in der er SRP auf eine bereits recht komplexe Codebasis angewendet hat, die jetzt sehr stark fragmentiert und schwer zu verstehen und zu debuggen ist.

Wir haben jetzt ein Problem nicht nur mit der Codefragmentierung, sondern auch mit der Kapselung, da Methoden innerhalb einer Klasse, die möglicherweise privat oder geschützt waren, als „Änderungsgrund“ eingestuft und in öffentliche oder interne Klassen und Schnittstellen extrahiert wurden, die entspricht nicht den Kapselungszielen der Anwendung.

Wir haben einige Klassenkonstruktoren, die über 20 Schnittstellenparameter übernehmen, sodass unsere IoC-Registrierung und -Auflösung zu einem Monster für sich wird.

Ich möchte wissen, ob es einen Ansatz gibt, der sich von SRP unterscheidet und mit dem wir einige dieser Probleme beheben können. Ich habe gelesen, dass es nicht gegen SOLID verstößt, wenn ich eine Reihe leerer gröberkörniger Klassen erstelle, die eine Reihe eng verwandter Klassen "umschließen", um einen zentralen Zugriffspunkt auf die Summe ihrer Funktionen zu bieten (dh eine kleinere nachzubilden) übermäßige Implementierung der SRP-Klasse).

Abgesehen davon kann ich mir keine Lösung vorstellen, die es uns ermöglicht, unsere Entwicklungsbemühungen pragmatisch fortzusetzen und gleichzeitig alle zufrieden zu stellen.

Irgendwelche Vorschläge ?

Dean Chalk
quelle
18
Das ist nur meine Meinung, aber ich denke, es gibt eine weitere Regel, die unter den verschiedenen Akronymen leicht vergessen wird - "Common Sense Principle". Wenn eine "Lösung" mehr Probleme schafft, als sie wirklich löst, stimmt etwas nicht. Ich gehe davon aus, dass ein komplexes Problem in einer Klasse enthalten ist, die sich um seine Kompliziertheit kümmert und dennoch relativ einfach zu debuggen ist - ich lasse es in Ruhe. Im Allgemeinen scheint mir Ihre „Verpackungs“ -Idee richtig zu sein, aber ich überlasse die Antwort jemandem, der mehr über das Thema Bescheid weiß.
Patryk Ćwiek
6
Was den "Grund zur Veränderung" betrifft, müssen nicht alle Gründe vorzeitig besprochen werden. Warten Sie, bis Sie das tatsächlich ändern müssen, und sehen Sie dann, was getan werden kann, um solche Änderungen in Zukunft einfacher zu machen.
62
Eine Klasse mit 20 Konstruktorparametern klingt für mich nicht sehr SRP!
MattDavey
1
Sie schreiben "... IoC-Registrierung und -Auflösung ..."; Das hört sich so an, als ob Sie (oder Ihr Teamleiter) denken, dass "IoC" und "Dependency Injection" (DI) dasselbe sind, was nicht stimmt. DI ist ein Mittel, um IoC zu erreichen, aber sicherlich nicht das einzige. Sie sollten sorgfältig analysieren, warum Sie IoC durchführen möchten. Wenn Sie Unit-Tests schreiben möchten, können Sie auch versuchen, das Service-Locator-Muster oder einfach Interface-Klassen ( ISomething) zu verwenden. IMHO, diese Ansätze sind viel einfacher zu handhaben als die Abhängigkeitsinjektion und führen zu besser lesbarem Code.
2
Jede hier gegebene Antwort wäre in einem luftleeren Raum. Wir müssten den Code sehen, um eine bestimmte Antwort zu geben. 20 Parameter in einem Konstruktor? Vielleicht fehlt Ihnen ein Objekt ... oder sie sind alle gültig. oder sie könnten zu einer Konfigurationsdatei gehören, oder sie könnten zu einer DI-Klasse gehören, oder ... Die Symptome klingen zweifellos verdächtig, aber wie bei den meisten Dingen in CS "kommt es darauf an" ...
Steven A. Lowe

Antworten:

85

Wenn Ihre Klasse 20 Parameter im Konstruktor hat, hört es sich nicht so an, als ob Ihr Team genau weiß, was SRP ist. Wenn Sie eine Klasse haben, die nur eines tut, wie hat sie 20 Abhängigkeiten? Das ist wie eine Angeltour und das Mitbringen einer Angelrute, einer Tackle Box, Quiltzubehör, einer Bowlingkugel, Nunchaks, eines Flammenwerfers usw. Wenn Sie all das brauchen, um angeln zu gehen, gehen Sie nicht nur angeln.

Das heißt, SRP kann, wie die meisten Prinzipien da draußen, übertrieben angewendet werden. Wenn Sie eine neue Klasse zum Inkrementieren von ganzen Zahlen erstellen, dann ist das vielleicht eine einzelne Verantwortung, aber kommen Sie schon. Das ist lächerlich. Wir neigen dazu zu vergessen, dass Dinge wie die SOLID-Prinzipien für einen Zweck da sind. SOLID ist ein Mittel zum Zweck, kein Selbstzweck. Das Ende ist Wartbarkeit . Wenn Sie das Prinzip der Einzelverantwortung so detailliert anwenden möchten, ist dies ein Indikator dafür, dass der Eifer für SOLID das Team für das Ziel von SOLID blind gemacht hat.

Also, ich denke, was ich sage, ist ... Die SRP ist nicht Ihr Problem. Es ist entweder ein Missverständnis der SRP oder eine unglaublich granulare Anwendung. Versuchen Sie, Ihr Team dazu zu bringen, die Hauptsache zur Hauptsache zu machen. Und das Wichtigste ist die Wartbarkeit.

BEARBEITEN

Bringen Sie die Benutzer dazu, Module so zu gestalten, dass sie einfacher zu bedienen sind. Stellen Sie sich jede Klasse als Mini-API vor. Überlegen Sie zunächst, wie diese Klasse verwendet werden soll, und implementieren Sie sie anschließend. Denken Sie nicht nur: "Was muss diese Klasse tun?" Die SRP hat eine große Tendenz, die Verwendung von Klassen zu erschweren, wenn Sie nicht viel über die Benutzerfreundlichkeit nachdenken.

BEARBEITEN 2

Wenn Sie nach Tipps zum Refactoring suchen, können Sie mit den von Ihnen vorgeschlagenen Schritten beginnen: Erstellen Sie grobkörnigere Klassen, um mehrere andere Klassen zusammenzufassen. Stellen Sie sicher, dass die grobkörnigere Klasse immer noch an der SRP festhält , jedoch auf einer höheren Ebene. Dann haben Sie zwei Alternativen:

  1. Wenn die feinkörnigeren Klassen an anderer Stelle im System nicht mehr verwendet werden, können Sie ihre Implementierung schrittweise in die gröberkörnigere Klasse ziehen und sie löschen.
  2. Lassen Sie die feinkörnigeren Klassen in Ruhe. Vielleicht wurden sie gut entworfen und Sie brauchten nur den Wrapper, um sie benutzerfreundlicher zu machen. Ich vermute, dass dies für einen Großteil Ihres Projekts der Fall ist.

Wenn Sie mit dem Refactoring fertig sind (aber bevor Sie sich auf das Repository festlegen), überprüfen Sie Ihre Arbeit und fragen Sie sich, ob das Refactoring tatsächlich zu einer Verbesserung der Wartbarkeit und Benutzerfreundlichkeit geführt hat.

Phil
quelle
2
Eine alternative Methode, um Leute zum Nachdenken über das Entwerfen von Klassen zu bewegen: Lassen Sie sie CRC-Karten schreiben (Klassenname, Verantwortung, Mitarbeiter) . Wenn eine Klasse zu viele Mitarbeiter oder Verantwortlichkeiten hat, ist es höchstwahrscheinlich nicht SRP-gerecht genug. Mit anderen Worten, der gesamte Text muss in die Karteikarte passen, sonst tut es zu viel.
Spoike
18
Ich weiß, wofür der Flammenwerfer ist, aber wie zum Teufel fischen Sie mit einer Stange?
R. Martinho Fernandes
13
+1 SOLID ist ein Mittel zum Zweck, kein Selbstzweck.
B Seven
1
+1: Ich habe vorhin argumentiert, dass Dinge wie "The Law of Demeter" falsch benannt sind, es sollte "The Guide Line of Demeter" sein. Diese Dinge sollten für Sie arbeiten, Sie sollten nicht für sie arbeiten.
Binary Worrier
2
@EmmadKareem: Es ist richtig, dass DAO-Objekte mehrere Eigenschaften haben sollen. Andererseits gibt es mehrere Dinge, die Sie zu einer einfachen CustomerKlasse zusammenfassen können und deren Code besser gewartet werden kann. Beispiele finden Sie hier: codemonkeyism.com/…
Spoike
33

Ich denke, es ist in Martin Fowlers Refactoring, dass ich einmal eine Gegenregel zu SRP gelesen habe, die definiert, wo es zu weit geht. Es gibt eine zweite wichtige Frage: "Hat jede Klasse nur einen Grund, sich zu ändern?" und das heißt: "Betrifft jede Änderung nur eine Klasse?"

Wenn die Antwort auf die erste Frage in jedem Fall "Ja" lautet, die zweite Frage jedoch "Nicht einmal annähernd" lautet, müssen Sie sich erneut überlegen, wie Sie SRP implementieren.

Wenn Sie beispielsweise ein Feld zu einer Tabelle hinzufügen, müssen Sie eine DTO- und eine Validierungsklasse sowie eine Persistenzklasse und ein Ansichtsmodellobjekt usw. ändern, und dann haben Sie ein Problem erstellt. Vielleicht sollten Sie überdenken, wie Sie SRP implementiert haben.

Vielleicht haben Sie gesagt, dass das Hinzufügen eines Felds der Grund ist, das Kundenobjekt zu ändern, aber das Ändern der Persistenzschicht (z. B. von einer XML-Datei in eine Datenbank) ist ein weiterer Grund, das Kundenobjekt zu ändern. Sie möchten also auch ein CustomerPersistence-Objekt erstellen. Aber wenn Sie dies so tun, dass das Hinzufügen eines Feldes NOCH eine Änderung des CustomerPersisitence-Objekts erfordert, worum ging es dann? Sie haben immer noch ein Objekt mit zwei Änderungsgründen - es ist einfach kein Kunde mehr.

Wenn Sie jedoch ein ORM einführen, können Sie die Klassen möglicherweise so einrichten, dass beim Hinzufügen eines Felds zum DTO automatisch die zum Lesen dieser Daten verwendete SQL geändert wird. Dann haben Sie gute Gründe, die beiden Anliegen zu trennen.

Zusammenfassend ist hier, was ich tendenziell tue: Wenn es ein ungefähres Gleichgewicht zwischen der Häufigkeit gibt, mit der ich "Nein" sage, gibt es mehr als einen Grund, dieses Objekt zu ändern, und der Häufigkeit, mit der ich "Nein" sage, wird diese Änderung durchgeführt mehr als ein Objekt betreffen ", dann denke ich, dass ich das richtige Gleichgewicht zwischen SRP und Fragmentierung habe. Aber wenn beide immer noch hoch sind, frage ich mich, ob ich Bedenken auf andere Weise trennen kann.

pdr
quelle
+1 für "Betrifft jede Änderung nur eine Klasse?"
18.
Ein verwandtes Problem, das ich nicht besprochen habe, ist, dass, wenn Tasks, die an eine logische Entität gebunden sind, in verschiedene Klassen fragmentiert werden, Code möglicherweise Verweise auf mehrere unterschiedliche Objekte enthalten muss, die alle an dieselbe Entität gebunden sind. Betrachten Sie beispielsweise einen Ofen mit den Funktionen "SetHeaterOutput" und "MeasureTemperature". Wenn der Ofen durch unabhängige Objekte HeaterControl und TemperatureSensor dargestellt würde, würde nichts ein TemperatureFeedbackSystem-Objekt daran hindern, einen Verweis auf die Heizung eines Ofens und einen Temperatursensor eines anderen Ofens zu speichern.
Supercat
1
Wenn stattdessen diese Funktionen in einer IKiln-Schnittstelle kombiniert würden, die von einem Kiln-Objekt implementiert wurde, müsste das TemperatureFeedbackSystem nur eine einzige IKiln-Referenz enthalten. Wenn ein Ofen mit einem unabhängigen Nachrüsttemperatursensor verwendet werden müsste, könnte man ein CompositeKiln-Objekt verwenden, dessen Konstruktor IHeaterControl und ITemperatureSensor akzeptierte und sie zur Implementierung von IKiln verwendete, aber eine solche absichtlich lose Zusammensetzung wäre im Code leicht erkennbar.
Supercat
24

Nur weil ein System komplex ist, heißt das noch lange nicht, dass Sie es komplizieren müssen . Wenn Sie eine Klasse haben, die zu viele Abhängigkeiten (oder Mitbearbeiter) hat:

public class MyAwesomeClass {
    public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
      // Assign it all
    }
}

... dann wurde es viel zu kompliziert und Sie folgen SRP nicht wirklich , oder? Ich wette, wenn Sie aufschreiben, was MyAwesomeClassauf einer CRC-Karte funktioniert , passt es nicht auf eine Karteikarte, oder Sie müssen in wirklich winzigen unleserlichen Buchstaben schreiben.

Was Sie hier gesehen haben, ist, dass Ihre Jungs stattdessen nur das Prinzip der Schnittstellentrennung befolgt haben und es vielleicht auf ein Extrem gebracht haben, aber das ist eine ganz andere Geschichte. Sie könnten argumentieren, dass es sich bei den Abhängigkeiten um Domänenobjekte handelt (was jedoch vorkommt). Eine Klasse, die 20 Domänenobjekte gleichzeitig verarbeitet, ist etwas zu umfangreich.

TDD liefert Ihnen einen guten Indikator dafür, wie viel eine Klasse leistet. Unverblümt; Wenn eine Testmethode Setup-Code enthält, dessen Schreiben ewig dauert (auch wenn Sie die Tests überarbeiten), müssen Sie MyAwesomeClasswahrscheinlich zu viel tun.

Wie lösen Sie dieses Rätsel? Sie verlagern die Zuständigkeiten auf andere Klassen. Es gibt einige Schritte, die Sie für eine Klasse ausführen können, bei der dieses Problem auftritt:

  1. Identifizieren Sie alle Aktionen (oder Verantwortlichkeiten), die Ihre Klasse mit ihren Abhängigkeiten ausführt.
  2. Gruppieren Sie die Aktionen nach eng verwandten Abhängigkeiten.
  3. Redelegate! Das heißt, jede der identifizierten Aktionen wird entweder neuen oder (was noch wichtiger ist) anderen Klassen zugeordnet.

Ein abstraktes Beispiel für die Umgestaltung von Verantwortlichkeiten

Lassen Sie Ceine Klasse sein , die mehrere Abhängigkeiten hat D1, D2, D3, D4dass Sie Refactoring müssen weniger verwenden. Wenn wir herausfinden, welche Methoden Cdie Abhängigkeiten aufrufen, können wir eine einfache Liste davon erstellen:

  • D1- performA(D2),performB()
  • D2 - performD(D1)
  • D3 - performE()
  • D4 - performF(D3)

Wenn wir uns die Liste ansehen, können wir das sehen D1und D2sind miteinander verwandt, da die Klasse sie irgendwie zusammen braucht. Wir können auch sehen, dass D4Bedürfnisse D3. Wir haben also zwei Gruppierungen:

  • Group 1- D1<->D2
  • Group 2- D4->D3

Die Gruppierungen sind ein Indikator dafür, dass die Klasse jetzt zwei Verantwortlichkeiten hat.

  1. Group 1- Eine zur Behandlung der aufrufenden zwei Objekte, die sich gegenseitig benötigen. Vielleicht können Sie Ihre Klasse Cdie Notwendigkeit beseitigen lassen, beide Abhängigkeiten zu behandeln, und eine von ihnen kann stattdessen diese Aufrufe behandeln. In dieser Gruppierung ist es offensichtlich, D1dass ein Verweis auf haben könnte D2.
  2. Group 2- Die andere Verantwortung benötigt ein Objekt, um ein anderes aufzurufen. Kann nicht D4behandeln D3statt Klasse? Dann können wir wahrscheinlich D3aus der Klasse Cstreichen, indem D4wir stattdessen die Anrufe erledigen lassen .

Nehmen Sie meine Antwort nicht in Stein gemeißelt, da das Beispiel sehr abstrakt ist und viele Annahmen macht. Ich bin mir ziemlich sicher, dass es weitere Möglichkeiten gibt, dies umzugestalten, aber zumindest könnten die Schritte Ihnen dabei helfen, eine Art Prozess zu entwickeln, um Verantwortlichkeiten zu verschieben, anstatt die Klassen aufzuteilen.


Bearbeiten:

Zu den Kommentaren sagt @Emmad Karem :

"Wenn Ihre Klasse 20 Parameter im Konstruktor hat, hört es sich nicht so an, als ob Ihr Team genau weiß, was SRP ist. Wenn Sie eine Klasse haben, die nur eines tut, wie hat sie 20 Abhängigkeiten?" Wenn Sie eine Customer-Klasse haben, ist es nicht ungewöhnlich, 20 Parameter im Konstruktor zu haben.

Es ist richtig, dass DAO-Objekte in der Regel viele Parameter haben, die Sie in Ihrem Konstruktor festlegen müssen, und die Parameter sind normalerweise einfache Typen wie Zeichenfolgen. Im Beispiel einer CustomerKlasse können Sie ihre Eigenschaften jedoch auch in anderen Klassen gruppieren, um die Arbeit zu vereinfachen. B. eine AddressKlasse mit Straßen und eine ZipcodeKlasse, die die Postleitzahl enthält und die Geschäftslogik wie die Datenüberprüfung übernimmt:

public class Address {
    private String street1;
    //...

    private Zipcode zipcode;

    // easy to extend
    public bool isValid() {
        return zipcode.isValid();
    }
}

public class Zipcode {
    private string zipcode;
    public bool isValid() {
        // return regex match that zipcode contains numbers
    }
}

Diese Sache wird im Blog-Beitrag "Niemals, niemals, niemals, niemals in Java (oder zumindest oft) String verwenden" weiter besprochen . Alternativ zur Verwendung von Konstruktoren oder statischen Methoden, um die Erstellung der Unterobjekte zu vereinfachen, können Sie ein Fluid Builder-Muster verwenden .

Spoike
quelle
+1: Tolle Antwort! Die Gruppierung ist IMO ein sehr leistungsfähiger Mechanismus, da Sie die Gruppierung rekursiv anwenden können. Grob gesagt können Sie mit n Abstraktionsebenen 2 ^ n Elemente organisieren.
Giorgio,
+1: Ihre ersten Absätze fassen genau zusammen, was mein Team erwartet. "Business-Objekte", die eigentlich Service-Objekte sind, und Code zum Einrichten von Komponententests, der Sie unbedingt schreiben sollten. Ich wusste, dass wir ein Problem hatten, wenn unsere Service-Layer-Aufrufe eine Codezeile enthielten. ein Aufruf einer Business-Layer-Methode.
Man
3

Ich bin mit allen Antworten über SRP einverstanden und wie es zu weit geführt werden kann. In Ihrem Beitrag erwähnen Sie, dass Sie aufgrund von "Über-Refactoring" zur Einhaltung von SRP festgestellt haben, dass die Verkapselung unterbrochen oder geändert wurde. Das einzige, was für mich funktioniert hat, ist, immer an den Grundlagen festzuhalten und genau das zu tun, was erforderlich ist, um ein Ziel zu erreichen.

Bei der Arbeit mit Legacy-Systemen ist der "Enthusiasmus", alles zu reparieren, um es zu verbessern, in Team-Leads normalerweise recht hoch, insbesondere bei denjenigen, die für diese Rolle neu sind. SOLID, hat nur kein SRP - Das ist nur das S. Vergewissern Sie sich, dass Sie, wenn Sie SOLID folgen, auch das OLID nicht vergessen.

Ich arbeite gerade an einem Legacy-System und wir sind am Anfang einen ähnlichen Weg gegangen. Was für uns funktioniert hat, war die Entscheidung eines kollektiven Teams , das Beste aus beiden Welten zu machen - SOLID und KISS (Keep It Simple Stupid). Gemeinsam haben wir wichtige Änderungen an der Codestruktur besprochen und den gesunden Menschenverstand bei der Anwendung verschiedener Entwicklungsprinzipien angewendet. Sie eignen sich hervorragend als Richtlinien, nicht als "Gesetze der S / W-Entwicklung". Im Team geht es nicht nur um den Teamleiter, sondern um alle Entwickler im Team. Was mir immer geholfen hat, ist, alle in einen Raum zu locken und gemeinsame Richtlinien zu entwickeln, denen sich Ihr gesamtes Team verpflichtet.

Wenn Sie ein VCS verwenden und Ihrer Anwendung nicht zu viele neue Funktionen hinzugefügt haben, können Sie jederzeit zu einer Codeversion zurückkehren, die das gesamte Team für verständlich, lesbar und wartbar hält. Ja! Ich bitte Sie, die Arbeit wegzuwerfen und von vorne zu beginnen. Dies ist besser, als zu "reparieren", was kaputt war, und es auf etwas zurückzubringen, das bereits existierte.

Sharath Satish
quelle
3

Die Antwort ist vor allem Wartbarkeit und Klarheit des Codes. Für mich bedeutet das, weniger Code zu schreiben , nicht mehr. Weniger Abstraktionen, weniger Schnittstellen, weniger Optionen, weniger Parameter.

Wann immer ich eine Code-Umstrukturierung auswerte oder eine neue Funktion hinzufüge, denke ich darüber nach, wie viel Boilerplate im Vergleich zur tatsächlichen Logik benötigt wird. Wenn die Antwort mehr als 50% beträgt, bedeutet dies wahrscheinlich, dass ich darüber nachdenke.

Neben SRP gibt es viele andere Entwicklungsstile. In deinem Fall fehlt es definitiv an YAGNI.

cmcginty
quelle
3

Viele der Antworten hier sind wirklich gut, konzentrieren sich jedoch auf die technische Seite dieser Ausgabe. Ich füge einfach hinzu, dass es sich so anhört, als ob der Entwickler versucht, dem SRP-Sound zu folgen, als ob er tatsächlich gegen das SRP verstößt.

Sie können Bobs Blog hier sehen über diese Situation sehen, aber er argumentiert, dass, wenn eine Verantwortlichkeit über mehrere Klassen hinweg verschmiert wird, die Verantwortlichkeits-SRP verletzt wird, weil sich diese Klassen parallel ändern. Ich vermute, Ihr Entwickler würde das Design am Anfang von Bobs Blog wirklich mögen, und es könnte ein bisschen enttäuscht sein, es auseinandergerissen zu sehen. Insbesondere, weil es gegen das "Common Closure Principle" verstößt - Dinge, die sich gemeinsam ändern, bleiben zusammen.

Denken Sie daran, dass sich die SRP auf "Änderungsgrund" bezieht und nicht auf "eine Sache tun", und dass Sie sich nicht mit diesem Änderungsgrund befassen müssen, bis eine Änderung tatsächlich eintritt. Der zweite bezahlt für die Abstraktion.

Jetzt gibt es das zweite Problem - den "virulenten Befürworter der SOLID-Entwicklung". Es hört sich sicher nicht so an, als ob Sie eine großartige Beziehung zu diesem Entwickler haben. Daher sind alle Versuche, ihn / sie von den Problemen in der Codebasis zu überzeugen, ratlos. Sie müssen die Beziehung reparieren, damit Sie eine echte Diskussion über die Probleme führen können. Was ich empfehlen würde, ist Bier.

Nein im Ernst - wenn Sie nicht trinken, gehen Sie in ein Café. Verlassen Sie das Büro und entspannen Sie sich an einem Ort, an dem Sie ungezwungen über diese Dinge sprechen können. Anstatt zu versuchen, bei einem Meeting ein Argument zu gewinnen, das Sie nicht wollen, haben Sie eine Diskussion, die irgendwo Spaß macht. Versuchen Sie zu erkennen, dass dieser Entwickler, der Sie in den Wahnsinn treibt, ein tatsächlich funktionierender Mensch ist, der versucht, Software "aus der Tür" zu holen und keinen Mist versenden möchte. Da Sie wahrscheinlich diese Gemeinsamkeiten teilen, können Sie mit der Erörterung beginnen, wie Sie das Design verbessern können, während Sie dennoch die SRP einhalten.

Wenn Sie beide anerkennen können, dass die SRP eine gute Sache ist, dass Sie Aspekte nur unterschiedlich interpretieren, können Sie wahrscheinlich produktive Gespräche beginnen.

Eric Smith
quelle
-1

Ich stimme Ihrer Entscheidung des Teamleiters [update = 2012.05.31] zu, dass SRP im Allgemeinen eine gute Idee ist. Aber ich stimme dem Kommentar von @ Spoike -s voll und ganz zu, dass ein Konstruktor mit 20 Schnittstellenargumenten viel zu viel ist. [/ Update]:

Die Einführung von SRP mit IoC verschiebt die Komplexität von einer "Multi-Responsible-Klasse" zu vielen SRP-Klassen und eine viel kompliziertere Initialisierung zum Nutzen von

  • Einfachere Unit-Testbarkeit / Tdd (Testen einer SRP-Klasse für sich)
  • aber auf Kosten von
    • eine wesentlich komplexere Code-Initialisierung und -Integration und
    • Schwierigeres Debuggen
    • Fragmentierung (= Verteilung des Codes auf mehrere Dateien / Verzeichnisse)

Ich fürchte, Sie können die Codefragmentierung nicht reduzieren, ohne srp zu opfern.

Sie können den Schmerz der Codeinitialisierung jedoch "lindern", indem Sie eine syntaktische Zuckerklasse implementieren, die die Komplexität der Initialisierung in einem Konstruktor verbirgt.

   class MySrpClass {
      MySrpClass(Interface1 parm1, Interface2 param2, .... Interface20 param2) {
      }
   } 

   class MySyntaxSugarClass : MySrpClass {
      MySyntaxSugarClass() {
         super(new MyInterface1Implementation(), new MyImpl2(), ....)
      }
   }
k3b
quelle
2
Ich glaube, 20 Schnittstellen sind ein Indikator dafür, dass die Klasse zu viel zu tun hat. Dh es gibt 20 Gründe für eine Änderung, was so ziemlich eine Verletzung von SRP ist. Nur weil das System komplex ist, muss es nicht kompliziert sein.
Spoike