Gibt es ein Entwurfsmuster, mit dem Sie nicht mehr nach Flags suchen müssen?

28

Ich werde einige Zeichenfolgen-Nutzdaten in der Datenbank speichern. Ich habe zwei globale Konfigurationen:

  • Verschlüsselung
  • Kompression

Diese können über die Konfiguration so aktiviert oder deaktiviert werden, dass entweder nur einer von ihnen aktiviert ist, beide aktiviert sind oder beide deaktiviert sind.

Meine aktuelle Implementierung ist folgende:

if (encryptionEnable && !compressEnable) {
    encrypt(data);
} else if (!encryptionEnable && compressEnable) {
    compress(data);
} else if (encryptionEnable && compressEnable) {
    encrypt(compress(data));
} else {
  data;
}

Ich denke über das Decorator-Muster nach. Ist es die richtige Wahl oder gibt es vielleicht eine bessere Alternative?

Damith Ganegoda
quelle
5
Was stimmt nicht mit dem, was Sie derzeit haben? Können sich die Anforderungen an diese Funktionalität ändern? IE, gibt es wahrscheinlich neue ifAussagen?
Darren Young
Nein, ich suche nach einer anderen Lösung, um den Code zu verbessern.
Damith Ganegoda
46
Du machst das rückwärts. Sie finden kein Muster und schreiben dann den zum Muster passenden Code. Sie schreiben den Code so, dass er Ihren Anforderungen entspricht, und verwenden dann optional ein Muster, um Ihren Code zu beschreiben .
Leichtigkeit Rennen mit Monica
1
Beachten Sie: Wenn Sie der Meinung sind, dass Ihre Frage tatsächlich ein Duplikat dieser Frage ist , haben Sie als Fragesteller die Möglichkeit, die letzte Wiedereröffnung zu "überschreiben" und sie als solche im Alleingang zu schließen. Ich habe das mit einigen meiner eigenen Fragen gemacht und es funktioniert wie ein Zauber. Hier ist, wie ich es gemacht habe, 3 einfache Schritte - der einzige Unterschied zu meinen "Anweisungen" ist, dass Sie, da Sie weniger als 3K Wiederholungen haben, durch den Flag-Dialog gehen müssen , um zur Option "duplizieren" zu gelangen
Gnat
8
@LightnessRacesinOrbit: In Ihren Aussagen steckt etwas Wahres, aber es ist durchaus vernünftig zu fragen, ob es einen besseren Weg gibt, den eigenen Code zu strukturieren, und es ist durchaus vernünftig, ein Entwurfsmuster aufzurufen, um eine vorgeschlagene bessere Struktur zu beschreiben. (Dennoch stimme ich zu, dass es ein bisschen ein XY-Problem ist, nach einem Entwurfsmuster zu fragen , wenn Sie ein Entwurf möchten, der einem bekannten Muster genau folgen kann oder nicht.) Außerdem ist es für "Muster" legitim, dies zu tun Wenn Sie ein bekanntes Muster verwenden, ist es oft sinnvoll, Ihre Komponenten entsprechend zu benennen.
Ruakh

Antworten:

15

Beim Entwerfen von Code haben Sie immer zwei Möglichkeiten.

  1. erledige es einfach, in diesem Fall wird so ziemlich jede Lösung für dich funktionieren
  2. Sei pedantisch und entwerfe eine Lösung, die die Macken der Sprache und ihrer Ideologie ausnutzt (OO-Sprachen in diesem Fall - die Verwendung des Polymorphismus als Mittel, um die Entscheidung zu treffen)

Ich werde mich nicht auf den ersten der beiden konzentrieren, denn es gibt wirklich nichts zu sagen. Wenn Sie es nur zum Laufen bringen möchten, können Sie den Code so lassen, wie er ist.

Aber was würde passieren, wenn Sie es auf umständliche Weise tun und das Problem mit Designmustern tatsächlich so lösen würden, wie Sie es wollten?

Möglicherweise sehen Sie sich den folgenden Prozess an:

Beim Entwerfen von OO-Code müssen die meisten ifin einem Code enthaltenen s nicht vorhanden sein. Wenn Sie zwei Skalartypen vergleichen möchten, z. B. ints oder floats, ist es wahrscheinlich, dass Sie einen ifhaben. Wenn Sie jedoch die Prozeduren basierend auf der Konfiguration ändern möchten, können Sie den Polymorphismus verwenden , um das zu erreichen, was Sie möchten ifs) von Ihrer Geschäftslogik zu einem Ort, an dem Objekte instanziiert werden - zu Fabriken .

Ab sofort kann Ihr Prozess 4 verschiedene Pfade durchlaufen:

  1. dataist weder verschlüsselt noch komprimiert (call nothing, return data)
  2. dataist komprimiert (call compress(data)and return it)
  3. dataist verschlüsselt (anrufen encrypt(data)und zurücksenden)
  4. datawird komprimiert und verschlüsselt (call encrypt(compress(data))and return it)

Wenn Sie sich nur die 4 Pfade ansehen, finden Sie ein Problem.

Sie haben einen Prozess, der 3 verschiedene Methoden aufruft (theoretisch 4, wenn Sie nichts als eine aufruft), die die Daten manipulieren und sie dann zurückgeben. Die Methoden haben unterschiedliche Namen , unterschiedliche sogenannte öffentliche APIs (die Art und Weise, wie die Methoden ihr Verhalten kommunizieren).

Mithilfe des Adaptermusters können wir die aufgetretene Namenskollision lösen (wir können die öffentliche API vereinen). Einfach gesagt, hilft der Adapter, dass zwei inkompatible Schnittstellen zusammenarbeiten. Außerdem definiert der Adapter eine neue Adapterschnittstelle, die Klassen, die versuchen, ihre API zu vereinen, implementieren.

Dies ist keine konkrete Sprache. Es handelt sich um einen generischen Ansatz, bei dem jedes Schlüsselwort für einen beliebigen Typ steht. In einer Sprache wie C # können Sie es durch generics ( <T>) ersetzen .

Ich gehe davon aus, dass Sie im Moment zwei Klassen haben können, die für die Komprimierung und Verschlüsselung verantwortlich sind.

class Compression
{
    Compress(data : any) : any { ... }
}

class Encryption
{
    Encrypt(data : any) : any { ... }
}

In einer Unternehmenswelt werden mit hoher Wahrscheinlichkeit sogar diese spezifischen Klassen durch Schnittstellen ersetzt, z. B. würde das classSchlüsselwort durch interface(sollten Sie sich mit Sprachen wie C #, Java und / oder PHP befassen) ersetzt, oder das classSchlüsselwort wird beibehalten Compressund EncryptMethoden würden als rein virtuell definiert , sollten Sie in C ++ programmieren.

Um einen Adapter herzustellen, definieren wir eine gemeinsame Schnittstelle.

interface DataProcessing
{
    Process(data : any) : any;
}

Dann müssen wir Implementierungen der Schnittstelle bereitstellen, um sie nützlich zu machen.

// when neither encryption nor compression is enabled
class DoNothingAdapter : DataProcessing
{
    public Process(data : any) : any
    {
        return data;
    }
}

// when only compression is enabled
class CompressionAdapter : DataProcessing
{
    private compression : Compression;

    public Process(data : any) : any
    {
        return this.compression.Compress(data);
    }
}

// when only encryption is enabled
class EncryptionAdapter : DataProcessing
{
    private encryption : Encryption;

    public Process(data : any) : any
    {
        return this.encryption.Encrypt(data);
    }
}

// when both, compression and encryption are enabled
class CompressionEncryptionAdapter : DataProcessing
{
    private compression : Compression;
    private encryption : Encryption;

    public Process(data : any) : any
    {
        return this.encryption.Encrypt(
            this.compression.Compress(data)
        );
    }
}

Auf diese Weise erhalten Sie 4 Klassen, von denen jede etwas völlig anderes ausführt, von denen jedoch jede dieselbe öffentliche API bereitstellt. Die ProcessMethode.

In Ihrer Geschäftslogik, in der Sie sich mit der Entscheidung Keine / Verschlüsselung / Komprimierung / Beides befassen, werden Sie Ihr Objekt so entwerfen, dass es von der zuvor von DataProcessinguns entworfenen Schnittstelle abhängt .

class DataService
{
    private dataProcessing : DataProcessing;

    public DataService(dataProcessing : DataProcessing)
    {
        this.dataProcessing = dataProcessing;
    }
}

Der Prozess selbst könnte dann so einfach sein:

public ComplicatedProcess(data : any) : any
{
    data = this.dataProcessing.Process(data);

    // ... perhaps work with the data

    return data;
}

Keine weiteren Bedingungen. Die Klasse DataServicehat keine Ahnung, was wirklich mit den Daten geschehen wird, wenn sie an das dataProcessingMitglied übergeben werden, und kümmert sich nicht wirklich darum, es liegt nicht in ihrer Verantwortung.

Im Idealfall sollten Sie die 4 von Ihnen erstellten Adapterklassen durch Komponententests testen lassen, um sicherzustellen, dass sie funktionieren. Sie müssen dann Ihren Test bestehen. Und wenn sie bestehen, können Sie ziemlich sicher sein, dass sie funktionieren, egal wo Sie sie in Ihrem Code aufrufen.

Wenn ich das so mache, habe ich nie mehr ifs in meinem Code?

Nein. Es ist weniger wahrscheinlich, dass Ihre Geschäftslogik Bedingun- gen enthält, aber diese müssen sich noch irgendwo befinden. Der Ort ist Ihre Fabriken.

Und das ist gut so. Sie trennen die Belange der Erstellung und der tatsächlichen Verwendung des Codes. Wenn Sie Ihre Fabriken zuverlässig machen (in Java können Sie sogar so weit gehen, dass Sie das Guice- Framework von Google verwenden), müssen Sie sich in Ihrer Geschäftslogik nicht darum kümmern, die richtige Klasse für die Injektion auszuwählen. Weil Sie wissen, dass Ihre Fabriken funktionieren und liefern, was gefragt wird.

Müssen all diese Klassen, Schnittstellen usw. vorhanden sein?

Dies bringt uns zurück zum Anfang.

Wenn Sie in OOP den Pfad für die Verwendung von Polymorphismus wählen, wirklich Entwurfsmuster verwenden, die Merkmale der Sprache ausnutzen und / oder dem Gedanken folgen möchten, dass alles eine Objektideologie ist, dann ist es das auch. Und selbst dann zeigt dieses Beispiel nicht alle Fabriken, die Sie benötigen. Wenn Sie die Klassen und umgestalten Compressionund Encryptionstattdessen Schnittstellen erstellen möchten, müssen Sie auch deren Implementierungen einbeziehen.

Am Ende stehen Ihnen Hunderte kleiner Klassen und Schnittstellen zur Verfügung, die sich auf ganz bestimmte Dinge konzentrieren. Was nicht unbedingt schlecht ist, aber möglicherweise nicht die beste Lösung für Sie ist, wenn Sie nur zwei Zahlen addieren möchten.

Wenn Sie es schnell erledigen möchten, können Sie sich die Lösung von Ixrec zulegen , die es zumindest geschafft hat, die else ifund -Blöcke zu beseitigen else, die meiner Meinung nach sogar ein bisschen schlechter sind als eine einfache Lösungif .

Beachten Sie, dass dies meine Art ist, gutes OO-Design zu machen. So habe ich es in den letzten Jahren gemacht und es ist der Ansatz, mit dem ich mich am wohlsten fühle.

Ich persönlich mag die Wenn-Weniger-Programmierung mehr und würde die längere Lösung über die 5 Codezeilen viel mehr schätzen. So bin ich es gewohnt, Code zu entwerfen, und ich kann ihn sehr gut lesen.


Update 2: Es gab eine wilde Diskussion über die erste Version meiner Lösung. Diskussion meistens von mir verursacht, wofür ich mich entschuldige.

Ich habe beschlossen, die Antwort so zu bearbeiten, dass dies eine der Möglichkeiten ist, die Lösung zu betrachten, aber nicht die einzige. Ich habe auch den Dekorationsteil entfernt, wo ich stattdessen Fassade meinte, was ich am Ende ganz weggelassen habe, weil ein Adapter eine Fassadenvariante ist.

Andy
quelle
28
Ich habe nicht abgelehnt, aber das Grundprinzip könnte die lächerliche Menge neuer Klassen / Interfaces sein, um etwas zu tun, was der ursprüngliche Code in 8 Zeilen tat (und die andere Antwort in 5). Meiner Meinung nach ist das Einzige, was damit erreicht wird, die Lernkurve für den Code zu erhöhen.
Maurycy
6
@ Maurycy Was OP bat, war zu versuchen, eine Lösung für sein Problem unter Verwendung gemeinsamer Entwurfsmuster zu finden, falls es eine solche Lösung gibt. Ist meine Lösung länger als der Code von Ixrec? Es ist. Das gebe ich zu. Löst meine Lösung sein Problem mithilfe von Entwurfsmustern und beantwortet so seine Frage und entfernt effektiv alle erforderlichen Wenns aus dem Prozess? Es tut. Ixrec's nicht.
Andy
26
Ich glaube, dass das Schreiben von Code, der klar, zuverlässig, präzise, performant und wartbar ist, der richtige Weg ist. Wenn ich für jedes Mal einen Dollar hätte, wenn jemand SOLID zitierte oder ein Softwaremuster zitierte, ohne seine Ziele und seine Gründe klar auszudrücken, wäre ich ein reicher Mann.
Robert Harvey
12
Ich glaube, ich habe zwei Probleme, die ich hier sehe. Erstens scheinen die Schnittstellen Compressionund Encryptionvöllig überflüssig. Ich bin mir nicht sicher, ob Sie vorschlagen, dass sie für den Dekorationsprozess irgendwie notwendig sind oder nur implizieren, dass sie extrahierte Konzepte darstellen. Das zweite Problem ist, dass das Bilden einer Klasse wie CompressionEncryptionDecoratorzu der gleichen Art von kombinatorischer Explosion führt wie die Bedingungen des OP. Ich sehe das Dekorationsmuster im vorgeschlagenen Code auch nicht deutlich genug.
cbojar
5
Bei der Debatte über SOLID vs. simple fehlt der Punkt: Dieser Code ist keiner und verwendet auch nicht das Dekorationsmuster. Code ist nicht automatisch SOLID, nur weil er eine Reihe von Schnittstellen verwendet. Die Abhängigkeitsinjektion einer DataProcessing-Schnittstelle ist ein bisschen nett; alles andere ist überflüssig. SOLID ist ein Unternehmen auf Architekturebene, das darauf abzielt, mit Veränderungen gut umzugehen. OP gab weder Informationen über seine Architektur noch darüber, wie er erwartet, dass sich sein Code ändert, so dass wir SOLID nicht einmal in einer Antwort diskutieren können.
Carl Leth
120

Das einzige Problem, das ich bei Ihrem aktuellen Code sehe, ist das Risiko einer kombinatorischen Explosion, wenn Sie weitere Einstellungen hinzufügen. Dies kann leicht durch eine Strukturierung des Codes wie folgt verringert werden:

if(compressEnable){
  data = compress(data);
}
if(encryptionEnable) {
  data = encrypt(data);
}
return data;

Mir ist kein "Designmuster" oder "Idiom" bekannt, für das dies ein Beispiel sein könnte.

Ixrec
quelle
18
@ DamithGanegoda Nein, wenn Sie meinen Code sorgfältig lesen, werden Sie feststellen, dass er in diesem Fall genau dasselbe tut. Das ist der Grund, warum elsezwischen meinen beiden if-Anweisungen kein Platz ist und warum ich sie datajedes Mal zuordne . Wenn beide Flags wahr sind, wird compress () ausgeführt, und encrypt () wird für das Ergebnis von compress () wie gewünscht ausgeführt.
Ixrec
14
@DavidPacker Technisch funktioniert das auch mit jeder if-Anweisung in jeder Programmiersprache. Ich habe mich für die Einfachheit entschieden, da dies wie ein Problem aussah, bei dem eine sehr einfache Antwort angemessen war. Ihre Lösung ist auch gültig, aber ich persönlich würde sie aufbewahren, wenn ich mehr als zwei Boolesche Flags zu befürchten habe.
Ixrec
15
@DavidPacker: Richtig ist nicht definiert, wie gut sich der Code an eine Richtlinie eines Autors über eine Programmierideologie hält. Richtig ist: "Tut der Code das, was er tun soll und wurde er in angemessener Zeit implementiert?" Wenn es Sinn macht, es "falsch" zu machen, dann ist der falsche Weg der richtige, denn Zeit ist Geld.
Whatsisname
9
@DavidPacker: Wenn ich in der Position des OP wäre und diese Frage stellen würde, wäre der Kommentar von Lightness Race in Orbit genau das, was ich wirklich brauche. "Mit Entwurfsmustern eine Lösung finden" beginnt bereits mit dem falschen Fuß.
Whatsisname
6
@DavidPacker Wenn Sie die Frage genauer lesen, besteht sie nicht auf einem Muster. Darin heißt es: "Ich denke über das Decorator-Muster nach. Ist es die richtige Wahl, oder gibt es vielleicht eine bessere Alternative?" . Sie haben den ersten Satz in meinem Zitat angesprochen, aber nicht den zweiten. Andere Leute gingen davon aus, dass nein, es nicht die richtige Wahl ist. Sie können dann nicht behaupten, dass nur Ihre Frage beantwortet.
Jon Bentley
12

Ich denke, Ihre Frage ist nicht auf Praktikabilität ausgerichtet. In diesem Fall ist die Antwort von lxrec die richtige, sondern auf Designmuster.

Offensichtlich ist das Befehlsmuster ein Overkill für ein so triviales Problem wie das, das Sie vorschlagen, aber zur Veranschaulichung hier ist es:

public interface Command {
    public String transform(String s);
}

public class CompressCommand implements Command {
    @Override
    public String transform(String s) {
        String compressedString=null;
        //Compression code here
        return compressedString;
    }
}

public class EncryptCommand implements Command {
    @Override
    public String transform(String s) {
        String EncrytedString=null;
        // Encryption code goes here
        return null;
    }

}

public class Test {
    public static void main(String[] args) {
        List<Command> commands = new ArrayList<Command>();
        commands.add(new CompressCommand());
        commands.add(new EncryptCommand()); 
        String myString="Test String";
        for (Command c: commands){
            myString = c.transform(myString);
        }
        // now myString can be stored in the database
    }
}

Wie Sie sehen, können die Befehle / Transformationen in einer Liste nacheinander ausgeführt werden. Offensichtlich werden beide ausgeführt, oder nur einer davon, abhängig davon, was Sie in die Liste aufgenommen haben, ohne if-Bedingungen.

Offensichtlich werden die Bedingungen in einer Fabrik enden, die die Befehlsliste zusammenstellt.

BEARBEITEN für den Kommentar von @ texacre:

Es gibt viele Möglichkeiten, die if-Bedingungen im Erstellungsbereich der Lösung zu umgehen. Nehmen wir zum Beispiel eine Desktop-GUI-App . Sie können Kontrollkästchen für die Komprimierungs- und Verschlüsselungsoptionen aktivieren. Bei on clicdiesen Kontrollkästchen instanziieren Sie den entsprechenden Befehl und fügen ihn der Liste hinzu oder entfernen ihn aus der Liste, wenn Sie die Option deaktivieren.

Tulains Córdova
quelle
Wenn Sie kein Beispiel für "eine Factory, die die Befehlsliste zusammenstellt" ohne Code angeben können, der im Wesentlichen wie die Antwort von Ixrec aussieht, wird die Frage durch IMO nicht beantwortet. Dies bietet eine bessere Möglichkeit zum Implementieren der Komprimierungs- und Verschlüsselungsfunktionen, jedoch nicht zum Vermeiden der Flags.
Thexacre
@thexacre Ich habe ein Beispiel hinzugefügt.
Tulains Córdova
Also haben Sie in Ihrem Checkbox-Ereignis-Listener "wenn checkbox.ticked dann add command"? Mir kommt es so vor, als würdest du nur die Fahne
mischen,
@thexacre Nein, ein Listener für jedes Kontrollkästchen. In dem Click - Ereignis gerade commands.add(new EncryptCommand()); oder commands.add(new CompressCommand());sind.
Tulains Córdova
Was ist mit dem Deaktivieren des Kontrollkästchens? In nahezu jedem Sprach- / UI-Toolkit, auf das ich gestoßen bin, muss der Status des Kontrollkästchens im Ereignis-Listener noch überprüft werden. Ich bin damit einverstanden, dass dies ein besseres Muster ist, aber es vermeidet nicht, im Grunde genommen etwas zu tun, wenn die Flagge irgendwo etwas tut.
thexacre
7

Ich denke, "Design Patterns" orientieren sich unnötigerweise an "Oo Patterns" und vermeiden ganz und gar einfachere Ideen. Wir sprechen hier von einer (einfachen) Datenpipeline.

Ich würde versuchen, es in Clojure zu tun. Jede andere Sprache, in der die Funktionen erstklassig sind, ist wahrscheinlich ebenfalls in Ordnung. Vielleicht könnte ich später ein C # -Beispiel machen, aber es ist nicht so schön. Meine Lösung wäre die folgenden Schritte mit einigen Erklärungen für Nicht-Clojurians:

1. Stellen Sie eine Reihe von Transformationen dar.

(def transformations { :encrypt  (fn [data] ... ) 
                       :compress (fn [data] ... )})

Dies ist eine Karte, dh eine Nachschlagetabelle / ein Wörterbuch / was auch immer, von Schlüsselwörtern zu Funktionen. Ein weiteres Beispiel (Schlüsselwörter zu Strings):

(def employees { :A1 "Alice" 
                 :X9 "Bob"})

(employees :A1) ; => "Alice"
(:A1 employees) ; => "Alice"

Also, schreiben (transformations :encrypt)oder (:encrypt transformations)würde die Verschlüsselungsfunktion zurückgeben. ( (fn [data] ... )Ist nur eine Lambda-Funktion.)

2. Holen Sie sich die Optionen als Folge von Schlüsselwörtern:

(defn do-processing [options data] ;function definition
  ...)

(do-processing [:encrypt :compress] data) ;call to function

3. Filtern Sie alle Transformationen mit den angegebenen Optionen.

(let [ transformations-to-run (map transformations options)] ... )

Beispiel:

(map employees [:A1]) ; => ["Alice"]
(map employees [:A1 :X9]) ; => ["Alice", "Bob"]

4. Kombiniere Funktionen zu einer:

(apply comp transformations-to-run)

Beispiel:

(comp f g h) ;=> f(g(h()))
(apply comp [f g h]) ;=> f(g(h()))

5. Und dann zusammen:

(def transformations { :encrypt  (fn [data] ... ) 
                       :compress (fn [data] ... )})

(defn do-processing [options data]
  (let [transformations-to-run (map transformations options)
        selected-transformations (apply comp transformations-to-run)] 
    (selected-transformations data)))

(do-processing [:encrypt :compress])

Das einzige, was sich ändert, wenn wir eine neue Funktion hinzufügen wollen, sagen wir "debug-print", ist das Folgende:

(def transformations { :encrypt  (fn [data] ... ) 
                       :compress (fn [data] ... )
                       :debug-print (fn [data] ...) }) ;<--- here to add as option

(defn do-processing [options data]
  (let [transformations-to-run (map transformations options)
        selected-transformations (apply comp transformations-to-run)] 
    (selected-transformations data)))

(do-processing [:encrypt :compress :debug-print]) ;<-- here to use it
(do-processing [:compress :debug-print]) ;or like this
(do-processing [:encrypt]) ;or like this
NiklasJ
quelle
Wie werden Funk-Funktionen aufgefüllt, um nur die Funktionen einzuschließen, die angewendet werden müssen, ohne im Wesentlichen eine Reihe von if-Anweisungen auf die eine oder andere Weise zu verwenden?
thexacre
Die Zeile funcs-to-run-here (map options funcs)filtert und wählt so eine Reihe von Funktionen aus, die angewendet werden sollen. Vielleicht sollte ich die Antwort aktualisieren und ein bisschen mehr ins Detail gehen.
NiklasJ
5

[Im Wesentlichen ist meine Antwort eine Fortsetzung der Antwort von @Ixrec oben . ]

Eine wichtige Frage: Wird die Anzahl der unterschiedlichen Kombinationen, die Sie abdecken müssen, zunehmen? Sie kennen Ihre Betreff-Domain besser. Dies ist Ihr Urteil.
Kann die Anzahl der Varianten möglicherweise wachsen? Das ist nicht unvorstellbar. Beispielsweise müssen Sie möglicherweise mehr unterschiedliche Verschlüsselungsalgorithmen berücksichtigen.

Wenn Sie damit rechnen, dass die Anzahl der verschiedenen Kombinationen zunimmt, kann Ihnen das Strategiemuster helfen. Es wurde entwickelt, um Algorithmen zu kapseln und eine austauschbare Schnittstelle zum aufrufenden Code bereitzustellen. Wenn Sie die entsprechende Strategie für jede bestimmte Zeichenfolge erstellen (instanziieren), steht Ihnen immer noch ein geringer logischer Aufwand zur Verfügung.

Sie haben oben kommentiert, dass Sie nicht erwarten, dass sich die Anforderungen ändern. Wenn Sie nicht damit rechnen, dass die Anzahl der Varianten zunimmt (oder wenn Sie dieses Refactoring aufschieben können), behalten Sie die Logik bei. Derzeit verfügen Sie über eine kleine und überschaubare Menge an Logik. (Geben Sie in den Kommentaren zu einem möglichen Refactoring eines Strategiemusters möglicherweise einen Hinweis für sich selbst an.)

Nick Alexeev
quelle
1

Eine Möglichkeit, dies in Scala zu tun, wäre:

val handleCompression: AnyRef => AnyRef = data => if (compressEnable) compress(data) else data
val handleEncryption: AnyRef => AnyRef = data => if (encryptionEnable) encrypt(data) else data
val handleData = handleCompression andThen handleEncryption
handleData(data)

Die Verwendung von Dekorationsmustern zur Erreichung der oben genannten Ziele (Trennung der einzelnen Verarbeitungslogiken und deren Verknüpfung) wäre zu ausführlich.

Wenn Sie ein Entwurfsmuster benötigen, um diese Entwurfsziele in einem OO-Programmierparadigma zu erreichen, bietet die funktionale Sprache native Unterstützung durch die Verwendung von Funktionen als erstklassige Bürger (Zeile 1 und 2 im Code) und als funktionale Komposition (Zeile 3).

Sachin K
quelle
Warum ist dies besser (oder schlechter) als der Ansatz des OP? Und / oder was halten Sie von der Idee des OP, ein Dekorationsmuster zu verwenden?
Kasper van den Berg
Dieses Code-Snippet ist besser und bezieht sich ausdrücklich auf die Bestellung (Komprimierung vor der Verschlüsselung).
Rag