Ist das Spezifikationsmuster sinnlos?

8

Ich frage mich nur, ob das Spezifikationsmuster sinnlos ist, das folgende Beispiel:

Wenn Sie beispielsweise überprüfen möchten, ob ein Kunde in seinem Konto über genügend Guthaben verfügt, erstellen Sie eine Spezifikation wie etwa:

%Vor%

Ich frage mich jedoch, ob ich die gleichen "Vorteile" des Spezifikationsmusters erzielen kann (indem ich zum Beispiel nur die Geschäftsregeln ändern muss), indem ich Property-Getter in der Customer-Klasse verwende:

> %Vor%

Vom Kundencode:

%Vor%

Also meine Frage ist wirklich; Was ist der Unterschied zwischen der Verwendung des Eigenschaftengetters zum Umschließen der Geschäftslogik und dem Erstellen der Specification-Klasse?

Danke euch allen im Voraus!

    
Henry Aung 15.12.2010, 02:27
quelle

4 Antworten

7

Weil Sie mit der Spezifikationsklasse neue Kriterien erstellen können, ohne die Objekte selbst zu ändern.

    
zerkms 15.12.2010, 02:33
quelle
9

Im Allgemeinen ist ein Specification-Objekt nur ein Prädikat, das in einem Objekt eingeschlossen ist. Wenn ein Prädikat sehr häufig mit einer Klasse verwendet wird, kann es sinnvoll sein, das Prädikat in die Klasse Methode verschieben zu übernehmen gilt für.

Dieses Muster kommt wirklich zur Geltung, wenn Sie etwas komplizierteres aufbauen:

%Vor%

und es weitergeben oder serialisieren; Es kann sogar noch sinnvoller sein, wenn Sie eine Art Benutzeroberfläche für die Spezifikationserstellung bereitstellen.

Das heißt, C # bietet mehr idiomatische Möglichkeiten, um diese Art von Dingen auszudrücken, wie zB Erweiterungsmethoden und LINQ:

%Vor%

Ich habe mit experimentellem Code experimentiert, der Spezifikationen in Expression s implementiert, mit sehr einfachen statischen Builder-Methoden.

%Vor%

Das heißt, das ist eine ganze Ladung Boilerplate, die keinen Mehrwert bringt! Diese Expression s betrachten nur öffentliche Eigenschaften, also könnte man genauso einfach ein einfaches altes Lambda verwenden! Wenn nun eine dieser Spezifikationen auf den nicht öffentlichen Status zugreifen muss, benötigen wir wirklich eine Builder-Methode mit Zugriff auf den nicht öffentlichen Status. Ich werde hier lastCreditScore als Beispiel verwenden.

%Vor%

Wir brauchen auch eine Möglichkeit, um eine Zusammenfassung dieser Spezifikationen zu erstellen - in diesem Fall eine Zusammensetzung, die alle Kinder als wahr voraussetzt:

%Vor%

Ich denke, ein Teil des Nachteils ist, dass es zu komplizierten Expression -Bäumen kommen kann. Zum Beispiel, dies zu konstruieren:

%Vor%

erzeugt einen Expression Baum, der so aussieht. (Dies sind leicht formatierte Versionen dessen, was ToString() zurückgibt, wenn es für Expression aufgerufen wird - beachten Sie, dass Sie die Struktur des Ausdrucks überhaupt nicht sehen könnten, wenn Sie nur einen einfachen Delegaten hätten! Ein paar Anmerkungen: a DisplayClass ist eine vom Compiler generierte Klasse, die lokale Variablen enthält, die in einem Closure erfasst wurden, um das Problem nach oben zu lösen ; und das gedumpte Expression verwendet ein einzelnes = -Zeichen, um den Vergleich der Gleichheit darzustellen, und nicht das typische == von C #.)

%Vor%

Unordentlich! Viele Aufrufe von unmittelbaren Lambdas und Referenzen auf die in den Builder-Methoden erstellten Verschlüsse. Durch Ersetzen von Verschlussreferenzen durch ihre erfassten Werte und β-Reduzieren der verschachtelten Lambdas (auch ich < a href="https://en.wikipedia.org/wiki/Lambda_calculus#.CE.B1-conversion"> α-konvertiert alle Parameternamen zu einzigartigen generierten Symbolen als Zwischenschritt zur Vereinfachung der β-Reduktion) Es entsteht ein viel einfacherer Expression tree:

%Vor%

Diese Expression -Bäume können dann weiter kombiniert, in Delegaten kompiliert, hübsch gedruckt, bearbeitet und an LINQ-Interfaces übergeben werden, die Expression trees verstehen (wie die von EF bereitgestellten) oder wie geht es Ihnen.

Nebenbei bemerkt: Ich habe einen albernen kleinen Mikro-Benchmark erstellt und festgestellt, dass die Eliminierung der Closure-Referenz einen bemerkenswerten Leistungseinfluss auf die Evaluationsgeschwindigkeit des Beispiels Expression hatte, wenn sie zu einem Delegierten kompiliert wurde fast in der Mitte (!), von 134,1ns bis 70,5ns pro Anruf auf der Maschine, die ich zufällig vor mir sitze. Auf der anderen Seite machte die β-Reduktion keinen erkennbaren Unterschied, vielleicht weil die Kompilation das sowieso macht. In jedem Fall bezweifle ich, dass ein konventioneller Spezifikationsklassensatz diese Art von Evaluierungsgeschwindigkeit für einen Verbund von vier Bedingungen erreichen könnte; Wenn solch ein konventioneller Klassensatz aus anderen Gründen erstellt werden müsste, wie zum Beispiel der Einfachheit des Builder-UI-Codes, wäre es ratsam, die Klasse Expression produzieren zu lassen, anstatt sie direkt auszuwerten, aber überlegen Sie zuerst, ob Sie sie benötigen das Muster überhaupt in C # - ich habe viel zu viel Spezifikation-überdosierten Code gesehen.

    
Jeffrey Hantin 15.12.2010 02:42
quelle
3

Siehe zerkms-Antwort, plus: Eine Spezifikation kann auch an abstrakten Typen wie Schnittstellen oder als generisches Objekt arbeiten, das für eine ganze Reihe von Objekten anwendbar ist.

Oder die Prüfung, die für den Kunden durchgeführt werden muss, hängt vom Kontext ab. Beispielsweise ist ein Kundenobjekt möglicherweise noch nicht für das Bezahlrollensystem gültig, aber es kann in der Datenbank während eines Prozesses zur weiteren Verarbeitung gespeichert werden, wenn sich der Benutzer erneut anmeldet. Mit den Spezifikationen können Sie Gruppen zusammengehöriger Prüfungen an einem zentralen Ort erstellen und den gesamten Satz je nach Kontext wechseln. In diesem Fall kombinieren Sie es zum Beispiel mit einem Fabrikmuster.

    
Stefan 15.12.2010 02:44
quelle
2

Ja, es ist sinnlos.

Der Wikipedia-Artikel kritisiert dieses Muster ausführlich. Aber ich sehe den größten Kritikpunkt ausschließlich als Inner-Plattform-Effekt . Warum den AND-Operator neu erfinden? Lesen Sie erneut den Wikipedia-Artikel für die ausführlichere Kritik.

Henry, Sie haben Recht, anzunehmen, dass die Eigenschaft Get überlegen ist. Warum scheuen Sie ein einfacheres, wohlverstandenes OO-Konzept für ein obskures "Muster", das in seiner Konzeption Ihre Frage nicht beantwortet? Es ist eine Idee, aber eine schlechte. Es ist ein Anti-Pattern, ein Muster, das gegen dich, den Coder, arbeitet.

Sie haben gefragt, was der Unterschied ist, aber eine nützlichere Frage ist, wann sollte ein Spezifikationsmuster verwendet werden?

Benutze niemals dieses Muster , das ist meine allgemeine Regel für dieses Muster.

Zuerst sollten Sie erkennen, dass dies keine generische Theorie ist, es ist nur ein spezifisches Muster; mit einer spezifischen Modellierung der Klassen {Specification, AndSpecification, ...}. Mit der breiteren domänengetriebenen Theorie können Sie dieses Muster aufgeben und haben immer noch überlegene Optionen, mit denen jeder vertraut ist: zum Beispiel gut benannte Objekte / Methoden / Eigenschaften, um Domänensprache und -logik zu modellieren.

Jeffrey sagte:

  

Ein Spezifikationsobjekt ist nur ein Prädikat, das in ein Objekt eingepackt ist

Das gilt für domänengesteuert, aber nicht speziell für das Spezifikationsmuster. Jeffrey beschreibt umfassend eine Situation, in der man einen IQueryable-Ausdruck dynamisch aufbauen möchte, damit er im Datenspeicher (SQL-Datenbank) effizient ausgeführt werden kann. Seine endgültige Schlussfolgerung ist, dass Sie das mit dem Spezifikationsmuster nicht so machen können, wie es vorgeschrieben ist. Jeffreys IQueryable-Ausdrucksbäume sind eine alternative Möglichkeit, logische Regeln zu isolieren und sie in verschiedenen Zusammensetzungen anzuwenden. Wie Sie anhand seines Beispielcodes sehen können, ist es sehr ausführlich und sehr umständlich zu arbeiten. Ich kann mir keine Situation vorstellen, die solche dynamischen Komposite auch erfordern würde. Und wenn nötig, gibt es viele andere Techniken, die einfacher sind.

Wir alle wissen, dass Sie die Leistung zuletzt optimieren sollten. Wenn Sie versuchen, Blutende Kante mit IQueryable-Ausdrucksbäumen zu erreichen, handelt es sich um eine Falle. Beginnen Sie stattdessen mit den besten Tools, zuerst einem einfachen und knappen Property Getter. Dann testen, bewerten und priorisieren, welche Arbeit bleibt.

Ich habe noch eine Situation, in der dieses Spezifikationsmuster notwendig / besser ist. Wenn ich auf vermeintliche Situationen stoße, werde ich sie hier auflisten und sie widerlegen. Wenn ich auf eine gute Situation stoße, überarbeite ich diese Antwort mit einem neuen Abschnitt.

RE: zerkms antwortet

  

Weil mit der Spezifikationsklasse neue Kriterien erstellt werden können [sic]   ohne Änderung der Objekte selbst.

C # berücksichtigt bereits solche Situationen:

  • Vererbung (im Allgemeinen), wo Sie dann die geerbte Klasse erweitern (das ist gut, wenn Sie nicht den Namespace / die Bibliothek besitzen, woher die Klasse kommt)
  • Überschreiben der Methode in Inheritance
  • Teilweise - großartig, wenn Sie Datenmodellklassen haben. Sie können neben [NotStored] -Eigenschaften hinzufügen und genießen Sie alle Glück der Zugriff auf die Informationen, die Sie direkt vom Objekt benötigen. Wenn Sie '.' IntelliSense teilt Ihnen mit, welche Mitglieder verfügbar sind.
  • Extension-Methoden sind großartig, wenn die Vererbung nicht praktikabel ist (die Architektur unterstützt sie nicht) oder wenn die Elternklasse versiegelt ist.

In Projekten, von denen ich übernehme, begegne ich Anti-Patterns wie Specification Pattern und mehr. Sie befinden sich oft in einem separaten Projekt / Bibliothek (die Überfragmentierung von Projekten ist eine andere schreckliche Praxis) und jeder hat zu viel Angst, Objekte zu erweitern.

    
Todd 30.09.2016 07:06
quelle