Frage Ersetzt funktionale Programmierung GoF-Designmuster?


Seit ich letztes Jahr F # und OCaml gelernt habe, habe ich eine große Anzahl von Artikeln gelesen, die darauf bestehen, dass Design Patterns (speziell in Java) eine Lösung für die fehlenden Features in Imperativsprachen darstellen. Ein Artikel, den ich gefunden habe macht einen ziemlich starken Anspruch:

Die meisten Leute, die ich getroffen habe, haben die gelesen   Design Patterns Buch von der Gang of   Vier. Irgendein selbst respektierender Programmierer   Ich werde dir sagen, dass das Buch ist   Sprache Agnostiker und die Muster   gelten für Softwareentwicklung in   allgemein, unabhängig von welcher Sprache   Sie nutzen. Dies ist eine noble Behauptung.   Leider ist es weit entfernt von   die Wahrheit.

Funktionale Sprachen sind extrem   ausdrucksvoll. In einer funktionalen Sprache   man braucht keine Designmuster   weil die Sprache wahrscheinlich so hoch ist   Ebene, enden Sie in Programmierung   Konzepte, die Design eliminieren   Muster alle zusammen.

Die Hauptmerkmale der funktionalen Programmierung sind Funktionen wie erstklassige Werte, Curry, unveränderliche Werte usw. Es scheint mir nicht offensichtlich zu sein, dass sich OO-Entwurfsmuster an all diese Merkmale annähern.

Außerdem scheint es mir in funktionalen Sprachen, die OOP unterstützen (wie F # und OCaml), offensichtlich, dass Programmierer, die diese Sprachen verwenden, die gleichen Entwurfsmuster verwenden würden, die für jede andere OOP-Sprache verfügbar sind. Tatsächlich verwende ich jeden Tag F # und OCaml, und es gibt keine auffallenden Unterschiede zwischen den Mustern, die ich in diesen Sprachen verwende, und den Mustern, die ich benutze, wenn ich in Java schreibe.

Ist die Behauptung wahr, dass funktionale Programmierung die Notwendigkeit von OOP-Entwurfsmustern eliminiert? Wenn ja, könnten Sie ein Beispiel für ein typisches OOP-Entwurfsmuster und dessen funktionales Äquivalent posten oder verlinken?


941
2017-11-29 20:08


Ursprung


Antworten:


Der Blogpost, den du zitiertest, übertreibt seinen Anspruch ein wenig. FP nicht beseitigen die Notwendigkeit von Designmustern. Der Begriff "Entwurfsmuster" wird nur selten in FP-Sprachen verwendet. Aber sie existieren. Funktionale Sprachen haben viele Best Practice-Regeln der Form "wenn Sie auf Problem X stoßen, verwenden Sie Code, der wie Y aussieht", was im Grunde ein Designmuster ist.

Es ist jedoch richtig, dass die meisten OOP-spezifischen Entwurfsmuster in funktionalen Sprachen ziemlich irrelevant sind.

Ich denke nicht, dass es besonders kontrovers sein sollte, Designmuster zu sagen Im Algemeinen nur existieren, um Mängel in der Sprache zu beheben. Und wenn eine andere Sprache das gleiche Problem trivial lösen kann, braucht diese andere Sprache kein Entwurfsmuster dafür. Benutzer dieser Sprache sind sich möglicherweise nicht einmal bewusst, dass das Problem auftritt existiertweil es in dieser Sprache kein Problem gibt.

Hier ist, was die Viererbande zu diesem Thema zu sagen hat:

Die Wahl der Programmiersprache ist wichtig, weil sie den Blickwinkel beeinflusst. Unsere Muster gehen von Smalltalk / C ++ - Sprachfunktionen aus, und diese Auswahl bestimmt, was einfach implementiert werden kann und was nicht. Wenn wir prozedurale Sprachen angenommen haben, könnten wir Entwurfsmuster namens "Vererbung", "Verkapselung" und "Polymorphismus" enthalten. In ähnlicher Weise werden einige unserer Muster direkt von den weniger gebräuchlichen objektorientierten Sprachen unterstützt. CLOS hat zum Beispiel mehrere Methoden, die die Notwendigkeit eines Musters wie Visitor verringern. In der Tat gibt es genug Unterschiede zwischen Smalltalk und C ++, was bedeutet, dass einige Muster einfacher in einer Sprache ausgedrückt werden können als die andere. (Siehe Iterator zum Beispiel.)

(Das obige Zitat stammt aus dem Buch Introduction to the Design Patterns, Seite 4, Absatz 3)

Die wichtigsten Merkmale von funktionalen   Programmierung gehören Funktionen wie   erstklassige Werte, Curry,   unveränderliche Werte usw. Es scheint nicht   offensichtlich für mich, dass OO Design-Muster   sind irgendwelche von denen   Eigenschaften.

Was ist das Befehlsmuster, wenn nicht eine Annäherung an erstklassige Funktionen? :) In einer FP-Sprache würden Sie einfach eine Funktion als Argument an eine andere Funktion übergeben. In einer OOP-Sprache müssen Sie die Funktion in einer Klasse einbinden, die Sie instanziieren und dann an die andere Funktion übergeben können. Der Effekt ist der gleiche, aber in OOP wird es ein Design-Muster genannt, und es braucht viel mehr Code. Und was ist das abstrakte Fabrikmuster, wenn nicht currying? Übergeben Sie die Parameter einer Funktion jeweils einzeln, um zu konfigurieren, welche Art von Wert ausgegeben wird, wenn Sie sie schließlich aufrufen.

Ja, mehrere GoF-Designmuster werden in FP-Sprachen überflüssig, weil es leistungsfähigere und einfacher zu verwendende Alternativen gibt.

Aber natürlich gibt es noch Designmuster, die sind nicht gelöst durch FP-Sprachen. Was ist das FP-Äquivalent eines Singleton? (Einen Moment außer Acht lassend, dass Singletons im Allgemeinen ein schreckliches Muster zu verwenden sind)

Und es funktioniert auch in beide Richtungen. Wie gesagt, FP hat auch seine Entwurfsmuster, die Leute denken normalerweise nicht an sie.

Aber du hast vielleicht Monaden überfahren. Was sind sie, wenn nicht ein Entwurfsmuster für den "Umgang mit dem globalen Staat"? Das ist ein Problem, das in OOP-Sprachen so einfach ist, dass dort kein gleichwertiges Designmuster existiert.

Wir brauchen kein Entwurfsmuster für "inkrementiere eine statische Variable" oder "lese von dieser Buchse", weil es genau das ist, was du machst machen.

In (reinen) funktionalen Sprachen sind Nebenwirkungen und ein veränderbarer Zustand unmöglich, es sei denn, man arbeitet mit dem "Entwurfsmuster" der Monade oder mit irgendeiner anderen Methode, um dasselbe zu ermöglichen.

Zusätzlich in funktionalen Sprachen   welche unterstützen OOP (wie F # und   OCaml), scheint mir das offensichtlich   Programmierer, die diese Sprachen verwenden   würde die gleichen Designmuster verwenden   gefunden zu jeder anderen OOP verfügbar   Sprache. In der Tat verwende ich gerade jetzt F #   und OCaml jeden Tag, und es gibt keine   markante Unterschiede zwischen den   Muster, die ich in diesen Sprachen verwende vs   die Muster, die ich benutze, wenn ich schreibe   Java.

Vielleicht weil du immer noch zwingend denkst? Viele Menschen, die ihr ganzes Leben lang mit Imperativsprachen zu tun hatten, haben es schwer, diese Gewohnheit aufzugeben, wenn sie eine funktionale Sprache ausprobieren. (Ich habe einige ziemlich witzige Versuche bei F # gesehen, wo wörtlich jeden Die Funktion war nur eine Folge von 'let'-Anweisungen, im Wesentlichen so, als ob Sie ein C-Programm genommen hätten, und ersetzte alle Semikolons durch' let '. :))

Eine andere Möglichkeit wäre, dass Sie nicht erkannt haben, dass Sie Probleme trivial lösen, die Entwurfsmuster in einer OOP-Sprache erfordern würden.

Wenn Sie currying verwenden oder eine Funktion als Argument an eine andere übergeben, halten Sie inne und überlegen Sie sich, wie Sie das in einer OOP-Sprache tun würden.

Ist die Behauptung wahr?   funktionale Programmierung eliminiert die   Notwendigkeit für OOP Design Patterns?

Ja. :) Wenn Sie in einer FP-Sprache arbeiten, benötigen Sie die OOP-spezifischen Entwurfsmuster nicht mehr. Aber Sie brauchen immer noch einige allgemeine Entwurfsmuster, wie MVC oder andere nicht-OOP-spezifische Sachen, und Sie brauchen stattdessen ein paar neue FP-spezifische "Entwurfsmuster". Alle Sprachen haben ihre Unzulänglichkeiten, und Entwurfsmuster sind normalerweise, wie wir um sie arbeiten.

Wie auch immer, Sie werden es vielleicht interessant finden, sich an "sauberen" FP-Sprachen zu versuchen, wie ML (mein persönlicher Favorit, zumindest zu Lernzwecken), oder Haskell, wo Sie nicht die OOP-Krücke haben, auf die Sie zurückgreifen können stehen vor etwas Neuem.


Wie erwartet, haben einige Leute meine Definition von Entwurfsmustern als "Flicken von Mängeln in einer Sprache" beanstandet, deshalb hier meine Rechtfertigung: Wie bereits gesagt, sind die meisten Entwurfsmuster für ein Programmierparadigma oder manchmal sogar für eine bestimmte Sprache spezifisch. Oft lösen sie nur solche Probleme existieren in diesem Paradigma (siehe Monaden für FP oder abstrakte Fabriken für OOP). Warum existiert das abstrakte Fabrikmuster nicht in FP? Weil das Problem, das es zu lösen versucht, dort nicht existiert. Wenn also ein Problem in OOP-Sprachen existiert, das in FP-Sprachen nicht existiert, dann ist dies eindeutig ein Mangel der OOP-Sprachen. Das Problem kann gelöst werden, aber Ihre Sprache tut dies nicht, aber Sie benötigen eine Menge Vorabcode von Ihnen, um es zu umgehen. Idealerweise möchten wir, dass unsere Programmiersprache auf magische Weise entsteht alle Probleme gehen weg. Jedes noch bestehende Problem ist im Prinzip ein Mangel der Sprache. ;)


950
2017-11-29 23:06



Ist die Behauptung wahr, dass funktionale Programmierung die Notwendigkeit von OOP-Entwurfsmustern eliminiert?

Funktionale Programmierung ist nicht dasselbe wie objektorientierte Programmierung. Objektorientierte Entwurfsmuster gelten nicht für die funktionale Programmierung. Stattdessen haben Sie funktionale Designmuster für die Programmierung.

Für die funktionale Programmierung werden Sie die OO-Designmusterbücher nicht lesen, Sie werden andere Bücher über FP-Entwurfsmuster lesen.

Sprache Agnostiker

Nicht völlig. Nur sprachunabhängig in Bezug auf OO-Sprachen. Die Entwurfsmuster gelten überhaupt nicht für prozedurale Sprachen. Sie sind in einem relationalen Datenbankentwurfskontext kaum sinnvoll. Sie gelten nicht beim Entwerfen einer Tabelle.

ein typisches OOP-Designmuster und sein funktionales Äquivalent?

Das obige sollte nicht existieren. Das ist, als ob man nach einem Stück Verfahrenscode fragt, der als OO-Code umgeschrieben wird. Ummm ... Wenn ich das ursprüngliche Fortran (oder C) in Java übersetze, habe ich nichts mehr getan, als es zu übersetzen. Wenn ich es komplett in ein OO-Paradigma umschreibe, sieht es nicht mehr so ​​aus wie das Original Fortran oder C - es wird nicht mehr erkennbar sein.

Es gibt keine einfache Zuordnung von OO Design zu funktionalem Design. Sie sind sehr unterschiedliche Sichtweisen auf das Problem.

Funktionale Programmierung (wie alle Stile der Programmierung) hat Entwurfsmuster. Relationale Datenbanken haben Entwurfsmuster, OO hat Entwurfsmuster, prozedurale Programmierung hat Entwurfsmuster. Alles hat Designmuster, sogar die Architektur von Gebäuden.

Entwurfsmuster - als Konzept - sind ein zeitloser Weg des Aufbaus, unabhängig von Technologie oder Problembereich. Für bestimmte Problembereiche und Technologien gelten jedoch spezifische Entwurfsmuster.

Jeder, der darüber nachdenkt, was er tut, wird Designmuster entdecken.


135
2017-11-29 20:15



Brians Kommentare über die enge Verbindung zwischen Sprache und Muster sind auf den Punkt gebracht,

Der fehlende Teil dieser Diskussion ist das Konzept des Idioms. Copliens Buch "Advanced C ++" hatte hier einen großen Einfluss. Lange bevor er Christopher Alexander und die Spalte ohne einen Namen (Und Sie können nicht vernünftig über Muster sprechen, ohne Alexander zu lesen), er sprach darüber, wie wichtig es ist, Idiom zu beherrschen, um wirklich eine Sprache zu lernen. Er benutzte String-Kopie in C als Beispiel, während (* von ++ = * bis ++); Sie können dies als einen Bandaid für ein fehlendes Sprachfeature (oder Bibliotheksfeature) sehen, aber was wirklich wichtig ist, ist, dass es eine größere Einheit des Denkens oder des Ausdrucks ist als alle seine Teile.

Das ist es, was Muster und Sprachen zu tun versuchen, damit wir unsere Absichten deutlicher ausdrücken können. Je reicher die Einheiten des Denkens sind, desto komplexer können die Gedanken sein, die du ausdrücken kannst. Ein reichhaltiges, gemeinsames Vokabular auf verschiedenen Skalen - von der Systemarchitektur bis zum Bit-Twiddling - ermöglicht uns intelligentere Gespräche und Gedanken darüber, was wir tun sollten.

Wir können auch als Individuen lernen. Was ist der ganze Sinn der Übung? Wir alle können Dinge verstehen und anwenden, die wir niemals selbst denken könnten. Sprachen, Frameworks, Bibliotheken, Muster, Idiome usw. haben ihren Platz darin, den intellektuellen Reichtum zu teilen.


40
2018-01-01 20:59



Das GOF-Buch bindet sich explizit an OOP - der Titel lautet Design Patterns - Elements of Reusable Objektorientierte Software (Schwerpunkt meiner.)


35
2017-11-24 15:49



Entwurfsmuster in der dynamischen Programmierung von Peter Norvig hat eine durchdachte Berichterstattung über dieses allgemeine Thema, obwohl es über "dynamische" Sprachen statt "funktional" (es gibt Überschneidungen).


32
2017-11-29 20:23



Hier ist ein weiterer Link, der dieses Thema diskutiert: http://blog.ezyang.com/2010/05/design-muster-in-haskel/

In seinem Blogbeitrag beschreibt Edward alle 23 Original-GoF-Muster in Bezug auf Haskell.


24
2017-10-19 08:15



Wenn Sie versuchen, dies auf der Ebene von "Entwurfsmustern" (allgemein) und "FP versus OOP" zu betrachten, werden die Antworten, die Sie finden, bestenfalls trübe sein.

Gehen Sie jedoch auf beiden Achsen tiefer und überlegen Sie sich spezifische Entwurfsmuster und spezifische Sprachfunktionen und die Dinge werden klarer.

So zum Beispiel einige spezifische Muster, wie Besucher, Strategie, Befehl, und Beobachter definitiv ändern oder verschwinden, wenn Sie eine Sprache mit verwenden algebraische Datentypen und Mustervergleiche, Verschlüsse, erstklassige Funktionenusw. Einige andere Muster aus dem GoF-Buch bleiben jedoch "hängen".

Im Allgemeinen würde ich sagen, dass bestimmte Muster im Laufe der Zeit durch neue (oder gerade steigende) Sprachmerkmale eliminiert werden. Dies ist der natürliche Verlauf des Sprachdesigns; Wenn Sprachen zu einem höheren Niveau werden, werden Abstraktionen, die zuvor nur in einem Buch mit Beispielen aufgerufen werden konnten, nun zu Anwendungen eines bestimmten Sprachmerkmals oder einer bestimmten Bibliothek.

(Nebenbei, hier ist ein letzter Blog Ich schrieb, die andere Links zu mehr Diskussion über FP und Design-Muster hat.)


17
2017-11-29 23:15



Norvigs Darstellung spielt auf eine Analyse an, die sie von allen GoF-Mustern gemacht haben, und sie sagen, dass 16 der 23 Muster einfachere Implementierungen in funktionalen Sprachen hatten oder einfach Teil der Sprache waren. So waren vermutlich mindestens sieben von ihnen entweder a) gleich kompliziert oder b) in der Sprache nicht vorhanden. Leider werden sie nicht aufgezählt!

Ich denke, es ist klar, dass die meisten "kreativen" oder "strukturellen" Muster in GoF nur Tricks sind, um die primitiven Systeme in Java oder C ++ dazu zu bringen, das zu tun, was Sie wollen. Aber der Rest ist eine Überlegung wert, egal in welcher Sprache Sie programmieren.

Einer könnte Prototyp sein; Obwohl es sich um eine grundlegende Vorstellung von JavaScript handelt, muss es in anderen Sprachen von Grund auf neu implementiert werden.

Eines meiner Lieblingsmuster ist das Null-Objekt-Muster: Stellen Sie die Abwesenheit von etwas als ein Objekt dar, das eine angemessene Art von nichts tut. Dies ist möglicherweise einfacher in einer funktionalen Sprache zu modellieren. Die eigentliche Errungenschaft ist jedoch der Perspektivwechsel.


15
2017-11-30 07:11



Ich würde sagen, wenn Sie eine Sprache wie Lisp mit ihrer Unterstützung für Makros haben, dann können Sie eigene domänenspezifische Abstraktionen erstellen, Abstraktionen, die oft viel besser sind als die allgemeinen Idiomlösungen.


15
2017-12-16 19:44