2009-03-07 13 views
15

Ich baue gerade ein spezialisiertes Ticketsystem bei der Arbeit um (hauptsächlich verwendet, um Leute mit Fehlern in Fernerkundungshardware zu unterstützen ...). Wie auch immer, ich habe mich gefragt, ob es eine gute Idee ist, viele Arten von Workflow-Aktivitäten im Konstruktor eines Objekts auszuführen.Ist die Geschäftslogik in Konstruktoren eine gute Idee?

Zum Beispiel gibt es derzeit so:

$ticket = new SupportTicket(
    $customer, 
    $title, 
    $start_ticket_now, 
    $mail_customer 
); 

sobald das Objekt erstellt wird, wird es eine Zeile in eine Datenbank gestellt, gehen und dem Kunden ein Bestätigungs-E-Mail-Mail, möglicherweise senden eine Textnachricht an den nächsten Techniker, etc.

Sollte ein Konstrukteur aus all dieser Arbeit feuern, oder etwas mehr wie folgt?

$ticket = new SupportTicket($customer, $title); 
$customer->confirmTicketMailed($ticket); 
$helpdesk->alertNewTicket($ticket); 

Wenn es hilft, werden die Objekte alle auf der Basis der Active Stil.

Ich denke, es kann eine Frage der Meinung sein, aber was halten Sie für das Beste?

+0

Sie können dies relevant finden: [Konstruktoren müssen Code-Free] (http: //www.yegor256. com/2015/05/07/ctors-muss-be-code-free.html) – yegor256

Antwort

38

Wenn der Konstruktor all diese Arbeit ausführt, dann kennt der Konstruktor viele andere Domänenobjekte. Dies erzeugt ein Abhängigkeitsproblem. Sollte die ticket wirklich über die Customer und die wissen? Wenn neue Features hinzugefügt werden, ist es nicht wahrscheinlich, dass dem Workflow neue Domänenobjekte hinzugefügt werden, und bedeutet dies nicht, dass unsere arme ticket über eine ständig wachsende Population von Domänenobjekten Bescheid wissen muss?

Das Problem mit Spinnenweben der Abhängigkeit wie folgt ist, dass eine Änderung des Quellcodes zu einem der Domain-Objekt Auswirkungen auf unsere arme ticket haben wird. Die ticket wird haben so viel Wissen des Systems, dass egal, was passiert, wird die ticket beteiligt sein. Sie werden böse if Anweisungen finden, die sich in diesem Konstruktor sammeln, die Konfigurationsdatenbank und den Sitzungsstatus überprüfen und so viele andere Dinge. Die ticket wird zu einer Gottklasse werden.

Ein anderer Grund, warum ich Konstrukteure, die Dinge tun, nicht mag, ist, dass sie die Objekte um sie herum sehr schwer zu testen machen. Ich schreibe gerne viele Mock-Objekte. Wenn ich einen Test gegen die customer schreibe, möchte ich es eine gespottete ticket übergeben. Wenn der Konstruktor ticket den Workflow und den Tanz zwischen customer und anderen Domänenobjekten steuert, dann ist es unwahrscheinlich, dass ich es ausspionieren kann, die customer zu testen.

Ich schlage vor, Sie lesen The SOLID Principles, ein Papier, schrieb ich vor einigen Jahren über Abhängigkeiten in objektorientierten Designs zu verwalten.

+0

Gott sprach direkt mit Sie fühlen sich gesegnet Robert C. Martin hat Ihre Antwort beantwortet: O –

4

Teilen Sie die Dinge auf. Wirklich, Sie wollen kein Ticket wissen, wie es E-Mail usw. senden soll. Das ist die Arbeit eines Dienstes wie Klasse.

[Update] Ich denke nicht, dass die vorgeschlagenen Fabrikmuster dafür gut sind. Eine Factory ist nützlich, wenn Sie verschiedene Implementierungen von Tickets erstellen möchten, ohne diese Logik in das Ticket selbst zu übernehmen (z. B. überladene Konstruktoren).

Werfen Sie einen Blick auf das im Domain-Driven Design vorgeschlagene Service-Konzept.

Dienstleistungen: Wenn eine Operation gehört nicht konzeptionell auf ein beliebiges Objekt. Nach den natürlichen Konturen des Problems können Sie diese Operationen in Services implementieren. Das Servicekonzept heißt in GRASP "Pure Fabrication".

4

Das Problem, aus einer OO-Design-Perspektive ist, nicht so sehr, ob diese Funktionalität in einem Konstruktor implementiert werden soll (wie auf dieser Klasse anderen Methoden im Gegensatz zu), aber ob die Supportticket-Klasse soll wissen, wie zu tun all diese Dinge.

Kurz gesagt, sollte die SupportTicket-Klasse ein Support-Ticket und nur ein Support-Ticket modellieren. Das Erstellen einer E-Mail, das Wissen, wie diese E-Mail an den Kunden gesendet wird, das Anhalten des Tickets für die Verarbeitung usw. sind allesamt Funktionen, die Sie aus Ihrer SupportTicket-Klasse entfernen und an anderer Stelle einkapseln sollten. Die Vorteile dabei sind eine geringere Kopplung, höhere Kohäsion und verbesserte Testbarkeit.

Werfen Sie einen Blick auf die Single Responsibility Principle, die die Vorteile dieser Vorgehensweise erklärt. Insbesondere ist this Blog ein guter Ort, um sich über SRP und die anderen Grundprinzipien des guten OO-Designs zu informieren.

+0

Dies trifft den Nagel auf den Kopf.Ob Sie eine Fabrik (wie andere Posts erwähnen) oder nicht ist irrelevant.Oder – Egwor

2

Die kurze Antwort ist nein.

In der Hardware-Design, wir hatten ein Sprichwort, "Setzen Sie kein Tor auf die Uhr oder die Reset-Linie - es verdeckt die Logik." Das gleiche gilt hier aus dem gleichen Grund. Die längere Antwort muss warten, aber siehe "ScreetchinglyObvious Code".

+0

Basierend auf dem Kontext hier, ist der Benutzer "Alistair" höchstwahrscheinlich [Alistair Cockburn] (https://en.wikipedia.org/wiki/Alistair_Cockburn). –

1

Eigentlich war ich noch nie mit einer der verfügbaren Antworten zufrieden, aber schauen wir uns sie an. Die Auswahlmöglichkeiten basieren auf zwei Bewertungsfragen:

E1. Wo gehört das Wissen zur Geschäftslogik?

E2. Wo wird der nächste Leser des Codes aussehen? (Screechingly Obvious Code)

Einige Möglichkeiten:

  • Im Client-Code (das Objekt, das "neue Supportticket" der Fall ist). Wahrscheinlich kennt sie die Geschäftslogik nicht oder sollte sie nicht kennen, sonst würden Sie diesen schicke Konstruktor nicht erstellen wollen. Wenn es der richtige Ort für die Business-Logik ist, dann sollte es sagen:

    $ticket = new SupportTicket($customer, $title); 
    
    handleNewSupportTicket($ticket, ...other parameters...) 
    

    wo, um E2 zu schützen „handlenewSupportTicket“ ist der Ort, an dem die Business-Logik definiert ist (und der nächste Programmierer kann leicht finde es).

  • In der Support-Ticket Objekt, als separate Geschäftsanruf. Persönlich bin ich nicht wirklich glücklich damit, denn es sind zwei Anrufe aus dem Client-Code, wo der geistige Gedanke eine Sache ist.

    $ticket = new SupportTicket($customer, $title); 
    
    $ticket -> handleNewSupportTicket(...other parameters...) 
    
  • In der Klasse von Support-Tickets.Hier wird erwartet, dass die Geschäftslogik im Support-Ticket-Bereich liegt, aber da neue Support-Tickets unbedingt sofort und nicht später behandelt werden müssen, darf diese wichtige Aufgabe niemandem vorenthalten werden, weder speziell noch Client-Code. Ich weiß nur, wie Klassenmethoden in Smalltalk codieren, aber ich werde einen Stich an Pseudo-Code nehmen:

    $ticket = SupportTicket.createAndHandleNewSupportTicket(...other parameters...) 
    

    Unter der Annahme, dass der Client-Code den Griff in die neuen Tickets für andere Zwecke benötigt (sonst das „$ ticket = "würde verschwinden".

    Ich mag das nicht besonders, weil andere Programmierer es nicht so natürlich finden, nach Geschäftslogik in Klassen- oder statischen Methoden zu suchen. Aber es ist die dritte Wahl. Die vierte Möglichkeit ist, wenn es einen anderen Ort gibt, an dem die Geschäftslogik glücklich residiert und andere Programmierer natürlich danach suchen, in welchem ​​Fall sie dort in eine Klasse/statische Funktion geht.

    $ticket = SupportTicketBusinessLogic.createAndHandleNewSupportTicket(...other params...) 
    

    , wo diese Klasse/statische Funktion benötigt, um die mehrere Anrufe der Fall ist. (Aber jetzt haben wir wieder die Möglichkeit, dass Tickets erstellt werden können und nicht richtig behandelt :(.

Verwandte Themen