it-swarm.com.de

Verstößt es gegen das Prinzip der Einzelverantwortung, wenn ein Objekt weiß, wie es sich selbst ändern kann?

Ich habe mit Beispielen wie einem Player-Objekt gearbeitet, das weiß, wie man seinen eigenen Status ändert.

Ein weiteres Beispiel ist ein Rechnungsobjekt, das wusste, wie seine Rechnungsgebühren mithilfe eines Algorithmus berechnet werden.

Verstößt dies gegen das Prinzip der Einzelverantwortung?

Wenn ja, ist das Erstellen eines InvoiceCalculators der richtige Ansatz, um den Algorithmus unterzubringen?

Und der PlayerStateChanger ist das eine gültige Sache.

8
GaVaRaVa

Ich bin zu dem Schluss gekommen, dass das "Prinzip der Einzelverantwortung" aufgrund seines verwirrenden Namens mehr schadet als nützt.

Das Prinzip besagt nicht, dass eine Klasse nur eine bestimmte Sache tun darf. Wenn das der Fall wäre, wäre es ein wirklich dummes Prinzip!

Ein Objekt, das weiß, wie man seinen Zustand ändert, ist vollkommen in Ordnung - tatsächlich werden viele argumentieren, dass nur das Objekt selbst in der Lage sein sollte, seinen Zustand zu ändern. Ein Rechnungsobjekt, das Rechnungsgebühren berechnen kann, klingt ebenfalls völlig in Ordnung.

Bei der SRP geht es darum, Bedenken zu trennen, die sich unabhängig voneinander ändern könnten. Ein Rechnungsobjekt, das Logik zum Rendern als PDF und Logik zum Berechnen von Gebühren enthält, würde gegen das Prinzip verstoßen, da sich das Aussehen und Layout der Rechnung ändern könnte unabhängig vom Algorithmus zur Berechnung der Gebühren.

(Beachten Sie, dass sich "Änderung" hier auf Änderungen der Anforderungen bezieht, die Entwickler dazu zwingen, Code zu ändern, und nicht auf Änderungen des Laufzeitstatus, was ein separates Problem darstellt.)

Die SRP kann dazu führen, dass zahlreiche winzige Klassen definiert werden, um sicherzugehen, dass die Trennung erfolgt. Aber das Prinzip sollte gegen sein Gegenstück abgewogen werden, dass Dinge, die sich tun im Einklang ändern - im Code gekoppelt werden. Dies wird als "niedrige Kopplung, aber hohe Kohäsion" bezeichnet.

20
JacquesB

Ich würde davon abraten, Datenobjekte (Domänenobjekte) zu entwerfen, die sich selbst ändern können.

Ich ziehe es vor, folgende Unterscheidung zu treffen:

  1. Domänenobjekte - Objekte, die Daten enthalten, z. Player, Invoice. Vorzugsweise unveränderlich, hängt jedoch von der App ab (z. B. für ein Spiel benötigen Sie möglicherweise veränderbare Objekte, um die Effizienz zu steigern).
  2. Objekte, die Geschäftslogik enthalten: InvoiceCalculator oder ein Bank Objekt, das Geld in Accounts einzahlt

Vorteile:

  • Sie können Domänenobjekte an verschiedene Threads übergeben, ohne Angst vor Race-Bedingungen oder der Notwendigkeit einer Thread-Synchronisierung zu haben, da diese unveränderlich sind
  • Sie können Domänenobjekte einfacher serialisieren
  • Sie können sie als Schlüssel in Sets und in Maps einfügen (wenn Sie equals/hashCode implementieren). Weil sie unveränderlich sind, sind Sie sicher zu tun.
  • Durch eine ähnliche Logik in einer Geschäftsklasse, z. Bankklasse, die Geld auf ein Konto abhebt/einzahlt:
    • Sie können diese Funktionalität leicht finden, wenn Sie danach suchen
    • Sie können den Zugriff von verschiedenen Threads aus besser steuern

Was ich oben beschreibe, wurde von Martin Fowler als anämisches Domänenmodell charakterisiert. Ich nenne es lieber Trennung von Bedenken .

Denken Sie daran, dass das Programmieren kein Dogma ist. Lassen Sie sich also nicht von denen entmutigen, die sagen könnten, dass es kein wahres OOP ist. OOP ist nur ein Paradigma - es gibt auch prozedurale und funktionale.

Als der Artikel 2003 geschrieben wurde, waren Multi-Core-Programme nicht so verbreitet wie heute. Heutzutage sind sie es, deshalb mag ich es, meine Datenobjekte einfach und unveränderlich zu halten und die Logik anderswo zu handhaben.

3
Minas Mina

Eine Software hat viele Stakeholder, und jeder dieser Stakeholder kann Änderungen an ihr anfordern.

Zum Beispiel hat eine Rechnungsanwendung mehrere Stakeholder:

  • Der Datenbankadministrator, der für die Speicherung der Rechnungsdaten verantwortlich ist
  • Der Chief Operations Officer (COO), der die gesetzlichen Anforderungen zur Besteuerung der einzelnen Artikel und zur Berechnung der Rabatte angibt
  • Der Buchhalter, der entscheidet, wie die Daten im gedruckten Rechnungsbericht dargestellt werden sollen

Ziel des Grundsatzes der Einzelverantwortung ist es, Sie dabei zu unterstützen, etwas von Stakeholder B zu bremsen, wenn Sie eine von Stakeholder A angeforderte Änderung vornehmen. Einzelverantwortung bedeutet, dass jede Klasse nur einer Person (oder besser Rolle) im Unternehmen gegenüber rechenschaftspflichtig sein muss.

Das Letzte, was Sie tun möchten, ist, dass der COO Sie anschreit, weil Sie die Berechnung der Steuer gebrochen haben, während Sie in ein anderes Datenbankschema migrieren oder das Layout des Berichts ändern, um den Sie der Buchhalter gebeten hat.

Speziell für den Rechnungsfall in Ihrem Beispiel würde das BCE-Modell empfehlen, zwischen anwendungsunabhängiger Logik und anwendungsspezifischer Logik zu unterscheiden. Eine Rechnung ist ein Geschäftsobjekt (auch bekannt als Domain). Dieses Objekt kann genauso gut von anderen Anwendungen innerhalb des Unternehmens verwendet werden, die mit Rechnungen zu tun haben. Methoden wie addItem (), removeItem () gehören zur Rechnungsklasse (die ein Geschäftsobjekt ist), da sie vom Anwendungsfall unabhängig sind. Code in Bezug auf einen bestimmten Anwendungsfall gehört nun zu einer anderen Klasse, die als Controller (oder Interaktor) bezeichnet wird. Ich würde sagen, dass die Berechnung der Gebühren Teil des Anwendungsfalls für die Abrechnung ist und wahrscheinlich separat ausgedrückt wird. Dies ist subjektiv, da man auch behaupten kann, dass diese Regeln nicht spezifisch für diese eine Anwendung oder diesen Anwendungsfall sind.

1
Lefteris E