Design in „gemischten“ Sprachen: objektorientiertes Design oder funktionale Programmierung?

11

In den letzten Jahren wurden die Sprachen, die ich gerne benutze, immer "funktionaler". Ich benutze jetzt Sprachen, die eine Art "Hybrid" sind: C #, F #, Scala. Ich entwerfe meine Anwendung gerne mit Klassen, die den Domänenobjekten entsprechen, und verwende Funktionsmerkmale, die das Codieren einfacher, übereinstimmender und sicherer machen (insbesondere beim Bearbeiten von Sammlungen oder beim Übergeben von Funktionen).

Die beiden Welten "kollidieren" jedoch, wenn es darum geht, Muster zu entwerfen. Das spezifische Beispiel, mit dem ich kürzlich konfrontiert wurde, ist das Beobachtermuster. Ich möchte, dass ein Produzent einen anderen Code benachrichtigt (die "Verbraucher / Beobachter", z. B. einen DB-Speicher, einen Logger usw.), wenn ein Artikel erstellt oder geändert wird.

Ich habe es anfangs "funktional" so gemacht:

producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed

Aber ich frage mich jetzt, ob ich einen "OO" -Ansatz verwenden soll:

interface IItemObserver {
  onNotify(Item)
}
class DBObserver : IItemObserver ...
class LogObserver: IItemObserver ...

producer.addObserver(new DBObserver)
producer.addObserver(new LogObserver)
producer.foo() //calls observer in a loop

Welches sind die Vor- und Nachteile der beiden Ansätze? Ich hörte einmal einen FP-Guru sagen, dass Designmuster nur aufgrund der Einschränkungen der Sprache vorhanden sind, und deshalb gibt es in funktionalen Sprachen so wenige. Vielleicht könnte dies ein Beispiel dafür sein?

EDIT: In meinem speziellen Szenario brauche ich es nicht, aber ... wie würden Sie das Entfernen und Hinzufügen von "Beobachtern" auf funktionale Weise implementieren? (Dh wie würden Sie alle Funktionen im Muster implementieren?) Übergeben Sie beispielsweise nur eine neue Funktion?

Lorenzo Dematté
quelle
Was ist mit Schauspielern?
Kiritsuku
Vergiss den Unterricht und all das OOPish nutzlose Zeug. Sie sollten stattdessen in Modulen denken (Inspiration finden Sie in den Sprachen SML und OCaml).
SK-Logik
@Antoras Wenn Sie einen Vergleich mit einem auf Schauspielern basierenden Ansatz könnten, wäre es mehr als willkommen :)
Lorenzo Dematté
2
@ dema80, OCaml ist ein perfektes Multi-Paradigma. Module haben überhaupt nichts mit funktionaler Programmierung zu tun. Es gibt zum Beispiel ein fortschrittliches Modulsystem in einer rein imperativen Ada. Und all der Ruhm, den OOP erlangt hat, sollte eigentlich in Module fließen - alles Gute in OOP sind nur verschiedene Formen der Simulation der Modulfunktionalität. Sie können all diese Klassen völlig vergessen und stattdessen ihre Syntax zum Ausdrücken von Modulen verwenden, indem Sie in Modulen und nicht in OOP denken. Übrigens, genau das hat Microsoft mit seiner mscorlib gemacht - nicht viel OOP, nur Module und Namespaces.
SK-Logik
1
Ich denke, die bessere Frage ist: "Gibt es Klarheit oder Organisation, die Sie verlieren, wenn Sie es auf FP-Weise tun?"
Djechlin

Antworten:

0

Dies ist ein gutes Beispiel für zwei verschiedene Ansätze, die den Gedanken tragen, eine Aufgabe außerhalb der für das aufrufende Objekt relevanten Grenze auszuführen.

Während in diesem Beispiel klar ist, dass Sie sich für den funktionalen Ansatz entscheiden sollten, hängt dies im Allgemeinen davon ab, wie komplex das Verhalten des aufgerufenen Objekts ist. Wenn es sich wirklich um ein komplexes Verhalten handelt, bei dem Sie häufig ähnliche Logik erneut anwenden und Funktionsgeneratoren nicht verwendet werden können, um sie klar auszudrücken, sollten Sie sich wahrscheinlich für die Klassenzusammensetzung oder -vererbung entscheiden, wo Sie möchten haben ein bisschen mehr Freiheit, vorhandenes Verhalten auf Ad-hoc-Basis wiederzuverwenden und zu erweitern.

Ein Muster, das ich beobachtet habe, ist jedoch, dass Entwickler normalerweise zunächst den funktionalen Ansatz wählen und sich erst dann für einen klassenbasierten Ansatz entscheiden, wenn die Nachfrage nach einem detaillierteren Verhalten entsteht. Ich weiß zum Beispiel, dass Django von funktionsbasierten Ansichten, Vorlagenladern und Testläufern zu klassenbasierten Ansichten überging, sobald die Vorteile und Anforderungen offensichtlich wurden, aber nicht vorher.

Filip Dupanović
quelle
Ich denke, diese Antwort ist ein bisschen falsch. Funktionale Programmierung programmiert nicht (nur mit) Funktionen. Die funktionale Programmierung verwendet ebenfalls Abstraktionen, daher gibt es hier keine Zweiteilung.
Doval
"Komposition oder Vererbung, bei der Sie etwas mehr Freiheit haben, vorhandenes Verhalten wiederzuverwenden und zu erweitern" Sie sprechen so, dass dies NICHT einer der Hauptvorteile von reinem FP ist. Modularität, Zusammensetzbarkeit und Wiederverwendbarkeit passieren natürlich, wenn Sie funktional codieren. Dies ist keine "OOP-Sache"
sara
@ FilipDupanović Ich bezog mich auf die Wiederverwendung und Erweiterung von vorhandenem Code. reine Funktionen sind wahrscheinlich die komponierbarsten Dinge in der gesamten Programmierung. Sie werden so geschrieben, als könnten Sie die Komplexität in einer rein funktionalen Umgebung nicht verwalten. Das Verwalten der Komplexität durch Zusammensetzen einfacher Teile zu größeren, aber dennoch einfachen und undurchsichtigen Teilen ist der gesamte Kern von FP, und viele würden argumentieren, dass es sogar viel besser darin ist als OOP. Ich bin nicht einverstanden mit der von Ihnen vorgebrachten Dichotomie zwischen "funktionalen Einzeilern, die VS-soliden OOP nicht skalieren, die kein Problem skalieren".
Sara
Sie sagten, dass FP beim Erstellen und Wiederverwenden von Code minderwertig sei und dass OOP bei komplexeren Anwendungen mehr oder weniger immer "besser" sei. Ich behaupte, dass dies einfach falsch und irreführend ist, und deshalb dachte ich, dass es eine Abwertung rechtfertigt. Ist das wirklich "puristischer Eifer"? Ich werde hier jedoch nicht weiter darauf eingehen, da Kommentare nicht für solche erweiterten Diskussionen gedacht sind.
Sara
5

Die funktionale Version ist viel kürzer, leichter zu warten, leichter zu lesen und im Allgemeinen in nahezu jeder erdenklichen Hinsicht weit überlegen.

Viele - obwohl alles andere als Muster - sollen den Mangel an Funktionen in OOP ausgleichen, wie z. B. Beobachter. Diese sind funktional weitaus besser modelliert.

DeadMG
quelle
Ich stimme zu und habe das gleiche Gefühl.
Lorenzo Dematté
Ich stimme zu und habe das gleiche Gefühl. Aber ich habe mit meinem Funktionscode "geschummelt": In meinem Fall ist alles, was ich brauche, aber was ist, wenn ich "Beobachter" hinzufügen und entfernen muss? Ich habe meine Frage bearbeitet
Lorenzo Dematté
5

Dein "FP-Guru" hat teilweise recht; Viele OO-Muster sind Hacks, um funktionale Dinge zu tun. (Seine Behauptung, dass dies der Grund ist, warum es nur wenige FP-Sprachen gibt, scheint bestenfalls zweifelhaft.) Die Muster Observer und Strategy versuchen, erstklassige Funktionen zu emulieren. Das Besuchermuster ist ein Hack, um den Mustervergleich zu simulieren. Ihr IItemObserverist nur eine Funktion in Verkleidung. Wenn Sie so tun, als ob es sich von jeder anderen Funktion unterscheidet, die einen Gegenstand übernimmt, kaufen Sie nichts.

Objekte sind nur eine Art der Datenabstraktion. Dieses Papier wird dazu beitragen, Licht in dieses Thema zu bringen. Objekte können nützlich sein, aber es ist wichtig zu erkennen, dass sie nicht für alles geeignet sind. Es gibt keine Zweiteilung; Es geht einfach darum, das richtige Werkzeug für den richtigen Job auszuwählen, und für die funktionale Programmierung müssen Sie in keiner Weise Objekte aufgeben. Abgesehen davon ist funktionale Programmierung mehr als nur die Verwendung von Funktionen. Es geht auch darum, Nebenwirkungen und Mutationen zu minimieren.

Doval
quelle
+1 für (IMO) eine gute Antwort. Vielen Dank auch für den Link zum Artikel: Ich hatte ihn vor einiger Zeit gelesen und dann verloren. Jetzt habe ich es wieder gefunden.
Giorgio
-1

Ich kann die Frage nicht wirklich beantworten, weil ich nicht gut in funktionalen Sprachen bin. Aber ich denke, Sie sollten sich weniger Gedanken über die Vorgehensweise machen, solange das, was Sie haben, funktioniert. Soweit ich weiß, können Sie das Observer-Muster hier sehr gut überspringen, solange Sie in Zukunft keine weiteren Listener hinzufügen oder die Listener während der Ausführung nicht ändern.

Ich bin nicht der Meinung, dass Entwurfsmuster "Einschränkungen" in OO-Sprachen ausgleichen. Sie sind dazu da, die OOP-Funktionen gut zu nutzen. Polymorphismus und Vererbung sind Merkmale, keine Einschränkungen. Entwurfsmuster nutzen diese Funktionen, um ein flexibles Design zu fördern. Sie können ein absolut Nicht-OO-Programm in einem OO erstellen. Mit viel Sorgfalt können Sie ein ganzes Programm schreiben, das Objekte enthält, die keinen Status haben und FP imitieren.

DPD
quelle
1
"Sie können mit viel Sorgfalt ein ganzes Programm schreiben, das Objekte enthält, die keinen Status haben und FP imitieren." Natürlich können Sie dies durch Disziplin auch in imperativen Sprachen tun. :) Im Allgemeinen denke ich nicht, dass Designmuster Einschränkungen ausgleichen sollen, sondern betrachte den Fall des Besuchermusters.
Lorenzo Dematté
Ich beschäftige mich auch nicht mit dem Code: Ich möchte jedoch mit anderen Programmierern über den Ansatz
sprechen
Jedes Mal, wenn Sie ein sich wiederholendes Muster erkennen, ist dies nichts anderes als ein Hinweis auf eine schwerwiegende Einschränkung Ihrer Sprache und Ihres Modellierungsrahmens. In einer idealen Welt soll es überhaupt keine Muster geben.
SK-Logik
@ SK-Logik: Leider ist die Welt nicht ideal und die reale Welt hat Muster. Aus diesem Grund haben OO-Sprachen Vererbung, um wiederholbaren Code zu implementieren, ohne sie erneut schreiben zu müssen. Reale Weltobjekte haben einen Status, deshalb gibt es ein Statusmuster
DPD
1
@ SK-Logik: Mir ist klar, dass einige Sprachen programmierbar sind und andere auch die Durchsetzung von Effekten aus dem Typsystem ermöglichen. Mein Punkt ist, dass "Muster" wie "OOP" ein leider missverstandener Begriff ist. Ein Muster ist etwas, das in ähnlichen, aber unterschiedlichen Formen immer wieder auftaucht und keine einheitliche Lösung zulässt . Manchmal können Sie diese Quasirepetitionen verschwinden lassen - Multimethoden machen Besucher- / Doppelversand überflüssig. Das bedeutet nicht, dass "alle Muster Anzeichen eines Mangels sind", denn das bedeutet, dass die reale Welt mangelhaft ist. Muster stammen aus der Architektur , nicht aus Software.
Frank Shearar