SOLID in Aktion – das Single-Responsibility-Prinzip

Dec 24 2022
„Eine Klasse sollte einen und nur einen Grund haben, sich zu ändern.“
Wir kennen das Sprichwort „Alleskönner, Meister in nichts“. Wir mögen es nicht, wenn wir am empfangenden Ende sind.
Foto von Robert Linder auf Unsplash

Wir kennen das Sprichwort „Alleskönner, Meister in nichts“. Wir mögen es nicht, wenn wir am empfangenden Ende sind. Wir mögen es auch nicht, wenn es zu jemandem gesagt wird, den wir lieben. Das sollte sich auch auf Dinge erstrecken, die wir lieben.

Wir Entwickler lieben die Codes, die wir erstellt haben. Wir sind stolz auf die Funktion, die wir schreiben, die Klasse, die wir entwerfen, die Komponente, das System und die Microservices … die wir entwickeln. Wir würden es daher nicht mögen, wenn sie zu „Alleskönnern, Master of None“ werden.

Daher sollten wir bei der Arbeit an neuen Kreationen immer das Single-Responsibility-Prinzip (SRP) im Auge behalten.

Es gibt 2 Versionen dieses Prinzips:

  • Jede Klasse sollte eine einzige Verantwortung haben
  • Eine Klasse sollte einen und nur einen Grund haben, sich zu ändern

Wiederverwendbar, wartbar und verständlich

Im ersten Artikel schlage ich vor, dass EmailServiceman sich nur mit dem Versenden von E-Mails befassen sollte, nicht mit dem Versenden von E-Mails und dem Aktualisieren des Bestellstatus.

EmailService und seine Verbände

Die Vorteile sind vielfältig:

  • EmailServiceist wiederverwendbarer . StockManagementServicekann es auch aufrufen, um E-Mail-Benachrichtigungen über „niedrige Lagerbestände“ auszulösen.
  • EmailServiceist wartungsfreundlicher . Wir müssen uns um eine sendEmail(EmailContent)Methode kümmern und um nichts anderes. Wir müssen weder die Auftragsverwaltungslogik kennen noch uns Gedanken darüber machen, wie sie sich ändern könnte, um einen neuen Status hinzuzufügen.
  • EmailServiceist verständlicher . Ohne auf eine einzige Codezeile zu schauen, wissen wir mit Zuversicht, dass sie nur für das Senden von E-Mails verantwortlich ist.

Wenn die Anwendung wächst, verlangen die Benutzer mehr. E-Mails müssen besser formatiert werden, z. B. in HTML, mit Designbildern und CSS. Das Entwicklungsteam beschloss, die komplexen E-Mail-Designs in Vorlagendateien zu erfassen, anstatt sie in Codes festzucodieren.

Auftragsbestätigung (Screenshot)

EmailContentwurde jetzt um 2 neue Felder erweitert: a enthält templateNameden Namen der Vorlagendatei und enthält templateValuesdie Werte von Platzhaltern, die in der Vorlage definiert sind (z. B. customerNameMaps to Huy, orderAmountMaps to 42.90, orderCurrencyMaps to SGD)

data class EmailContent (
   // existing fields
   val fromEmail: String, 
   val toEmail: String, 
   val ccEmail: String,
   val emailBoy: String,
   // new fields
   val templateName: String,   
   val templateValues: Map<String, Object> 
)

interface EmailService {
    fun sendEmail(content: EmailContent): String
    fun sendEmailWithTemplate(content: EmailContent): String    
}

Obwohl dieser Verstoß immer noch leicht zu erkennen ist, ist er ein häufiger Fallstrick. So gut das ursprüngliche Design auch ist, die Weiterentwicklung von Funktionen ist oft an der Verletzung der SRP beteiligt.

Das kleinere Übel

Das Geschäft blüht auf und es wurden 2 weitere Bestellarten hinzugefügt. Zusätzlich zur ursprünglichen Warenkorbbestellung können Bestellungen von verbundenen Unternehmen oder monatlich wiederkehrenden Abonnements erfolgen. Unabhängig vom Modus erwarten Kunden Bestätigungs-E-Mails, wenn eine Bestellung erstellt und ihre Kreditkarte belastet wird.

Um zu vermeiden, dass die Codes zum Erstellen und Senden von Bestätigungs-E-Mails in 3 verschiedenen Klassen wiederholt werden, entschied sich das Team, sie in EmailServicedie sendOrderConfirmation(OrderDetail)Methode von zu verschieben

Vermeiden Sie Codeduplizierung oder halten Sie sich an die SRP

Dies ist ein offensichtlicher Verstoß gegen die SRP, aber es gibt starke Gründe dafür. Was ist das kleinere Übel, Code-Duplizierung in 3 Klassen oder 1 mehr Verantwortung in EmailService?

Anthologische Verantwortung

Haben Sie The Ballad of Buster Scruggs gesehen, einen Anthologiefilm der Coen-Brüder? Es ist eine Sammlung von 6 Western-Kurzgeschichten.

Lassen Sie uns analog eine Anthologie-Klasse haben. Die vielleicht besten Beispiele sind Utility-Klassen.

Ein gemeinsames Thema verbindet meist die einzelnen Gründe innerhalb der größeren Gruppe. StringUtils sammelt beispielsweise viele Methoden zur Manipulation von Zeichenfolgen.

Was ist, wenn es kein klares Thema gibt? A MiscellaneousUtils(oder seine kürzere Form MiscUtils) ist in Ordnung.

Fazit

Das Single-Responsibility-Prinzip bringt viele Vorteile mit sich. Wiederverwendbarkeit, Wartbarkeit und Verständlichkeit sind nur einige davon.

SRP ist leider nicht immer erreichbar oder wartbar. Wir haben zwei Szenarien analysiert, in denen sich trotz eines guten anfänglichen Designs immer noch Verstöße einschleichen.

Wir haben nicht darüber gesprochen, wie diese Verstöße behoben werden können. Das ist Absicht, denn sie zu beheben ist genauso schwer wie die technischen Schulden zu beheben, die jedes Team im Laufe der Zeit anhäuft.

Last but not least diskutieren wir die Gebrauchsklasse als eine Form anthologischer Verantwortung.

Wenn Ihnen dieser Artikel gefällt, folgen Sie mir bitte für mehr qualitativ hochwertige Inhalte.

Weitere Artikel dieser Serie: