2012-05-12 4 views
15

Der Schöpfer der Clojure Sprache claims, dass "open, und große Reihe von Funktionen arbeiten auf eine offene, und kleine, erweiterbare Abstraktionen ist der Schlüssel zur algorithmischen Wiederverwendung und Bibliotheksinteroperabilität ". Offensichtlich widerspricht es dem typischen OOP-Ansatz, bei dem Sie viele Abstraktionen (Klassen) und eine relativ kleine Gruppe von Funktionen erzeugen, die auf ihnen arbeiten. Bitte legen nahe, ein Buch, ein Kapitel in einem Buch, einen Artikel oder Ihre persönlichen Erfahrungen, die zu den Themen erarbeiten:"Viele Funktionen, die auf wenigen Abstraktionen arbeiten" -Prinzip vs OOP

  1. Beispiele für Probleme zu motivieren, die in OOP erscheinen und wie mit „viele Funktionen auf wenige Abstraktionen“ würde adressieren sie
  2. , wie man effektiv MFUFA tun * Design
  3. wie OOP Code zu MFUFA Refactoring
  4. wie OOP-Sprachen Syntax in die Quere kommt von MFUFA

* MFUFA: „viele Funktionen auf wenige Abstraktionen“

+0

Die [Ausdruck Problem] (http://c2.com/cgi/wiki?ExpressionProblem) scheint zu sein, was Sie wollen. –

+0

ein interessantes Video zum Thema: http://www.infoq.com/presentations/Clojure-The-Art-of-Abstraction – mikera

+0

ich nicht dieses Zitat in dem Artikel –

Antwort

0

Ich denke, Sie bekommen nicht, dass ein Unterschied zwischen Bibliotheken und Programme gibt.

OO-Bibliotheken, die gut funktionieren, erzeugen normalerweise eine kleine Anzahl von Abstraktionen, die die Programme verwenden, um die Abstraktionen für ihre Domäne zu erstellen. Größere OO-Bibliotheken (und Programme) verwenden Vererbung, um verschiedene Versionen von Methoden zu erstellen und neue Methoden einzuführen.

Also, ja, das gleiche Prinzip gilt für OO-Bibliotheken.

+2

nicht einverstanden finden kann, ist der Unterschied real in Client-Code auch. Wo ich alle meine Daten in Clojure modelliere, indem ich nur Maps und Seqs benutze, bin ich in Java gezwungen, ständig neue typsichere Container für jeden Anwendungsfall zu erfinden. Man sollte beachten, dass der Hauptunterschied nicht zwischen OOP und FP besteht, sondern zwischen statischer und dynamischer Typisierung. –

+2

Marko, 1. Ich stimme Ihnen zu, dass der Unterschied auch im Client-Code liegt. 2. Ich stimme nicht zu, es geht um statische oder dynamische Typisierung. Derzeit programmiere ich in Ruby. Im Vergleich zu Java wurde der Code drei Mal kleiner, aber die großräumige Struktur ist die gleiche: Wir haben viele Klassen mit einigen Methoden, die an diese Klassen gekoppelt sind. Sehr wenig Wiederverwendung. Ich mag das nicht – Alexey

+0

@Alexey Ja, als ich über meine Worte nachdachte und Ruby/Rails in Betracht zog, kam ich zu den gleichen Schlussfolgerungen. Ruby kann Sie zwar nicht zwingen, Klassen zu verwenden, aber es treibt Sie definitiv in diese Richtung und weg von einer sauberen Trennung von Datenstrukturen und Code. In Clojure ist es umgekehrt - Sie haben immer noch Klassen (gut, Aufzeichnungen oder was auch immer), aber die erste Wahl ist immer map/vector/seq. –

18

Es gibt zwei Haupt Begriffe "Abstraktion" in der Programmierung:

  1. Parametrierung ("Polymorphismus", Generizität).
  2. Kapselung (Daten verstecken),

[Edit: Diese beiden duals sind. Die erste ist clientseitige Abstraktion, die zweite Implementierer-Seite Abstraktion (und im Falle interessieren Sie sich über diese Dinge: in Bezug auf die formale Logik oder Typentheorie, sie entsprechen universal und existentielle Quantifizierung bzw.).]

In OO ist die Klasse die Küchenspüle, um beide Arten der Abstraktion zu erreichen.

Ad (1), für fast jedes "Muster" müssen Sie eine benutzerdefinierte Klasse (oder mehrere) definieren. In der funktionalen Programmierung haben Sie dagegen oft leichtere und direktere Methoden, um die gleichen Ziele zu erreichen, insbesondere Funktionen und Tupel. Es wird oft darauf hingewiesen, dass die meisten "Entwurfsmuster" der GoF zum Beispiel in FP redundant sind.

Anzeige (2), Einkapselung wird ein wenig seltener benötigt, wenn Sie nicht überall einen veränderlichen Zustand haben, den Sie im Zaum halten müssen. Sie bauen immer noch ADTs in FP, aber sie sind tendenziell einfacher und allgemeiner, und daher brauchen Sie weniger von ihnen.

+2

Diese Unterscheidung ist fundamental, und jeder sollte diesen Artikel von Scott Meyers über die Kapselung lesen (http://www.drdobbs.com/cpp/184401197). – Daimrod

6

Eine viel frühere Version des Zitats:

„Die einfache Struktur und natürliche Anwendbarkeit von Listen werden in Funktionen wider, die erstaunlich nonidiosyncratic sind in Pascal die Fülle von declarable Datenstrukturen eine Spezialisierung, die hemmt innerhalb von Funktionen induziert. und benachteiligt zufällige Zusammenarbeit. Es ist besser, wenn 100 Funktionen auf einer Datenstruktur arbeiten, als 10 Funktionen auf 10 Datenstrukturen zu betreiben. "

... stammt aus dem Vorwort zum berühmten SICP Buch. Ich glaube, dieses Buch enthält viel anwendbares Material zu diesem Thema.

9

Wenn Sie ein Programm im objektorientierten Stil schreiben, legen Sie Wert auf Ausdruck Domänenbereich in Bezug auf die Datentypen. Und auf den ersten Blick sieht das nach einer guten Idee aus - wenn wir mit Benutzern arbeiten, warum nicht eine Klasse User haben? Und wenn Benutzer Autos verkaufen und kaufen, warum nicht Klasse Car haben? Auf diese Weise können wir einfach Daten verwalten und den Fluss steuern - es spiegelt nur die Reihenfolge der Ereignisse in der realen Welt wider. Während dies ist sehr bequem für Domain, für viele interne Objekte (das heißt Objekte, die nichts von der realen Welt widerspiegeln, sondern nur in der Programmlogik auftreten) Objekte ist es nicht so gut. Vielleicht das beste Beispiel ist eine Anzahl von Sammlungstypen in Java. In Java (und vielen anderen OOP-Sprachen) gibt es beide Arrays, List s. In JDBC gibt es ResultSet, die auch Art der Sammlung ist, aber Collection Schnittstelle nicht implementiert. Für die Eingabe verwenden Sie oft , die Schnittstelle für den sequentiellen Zugriff auf die Daten bietet - genau wie verknüpfte Liste! Es implementiert jedoch auch keine Art von Sammlungsschnittstelle. Wenn Ihr Code also mit der Datenbank arbeitet und ResultSet verwendet, ist es schwieriger, ihn für Textdateien und InputStream umzuformatieren.

MFUFA Prinzip lehrt uns weniger Aufmerksamkeit zu schenken Definition eingeben und mehr zu gemeinsamen Abstraktionen. Aus diesem Grund führt Clojure eine einzige Abstraktion für alle erwähnten Arten ein - die Sequenz. Jedes iterable wird automatisch zur Sequenz gezwungen, Streams sind nur faule Listen und Ergebnismenge kann leicht in einen der vorherigen Typen transformiert werden.

Ein weiteres Beispiel PersistentMap Schnittstelle für structs und Aufzeichnungen verwendet. Mit solchen allgemeinen Schnittstellen wird es sehr einfach, wieder verwendbare Subroutinen zu erstellen und nicht viel Zeit für das Refactoring aufzuwenden.

Zusammengefasst und Ihre Fragen zu beantworten:

  1. Ein einfaches Beispiel für ein Problem, das häufig in OOP erscheint: Daten aus vielen verschiedenen Quellen zu lesen (zB DB, Datei, Netzwerk, etc.) und die Verarbeitung in in der gleichen Weise.
  2. Um ein guten MFUFA Design Versuch machen zu Abstraktionen so häufig wie möglich zu machen und Ad-hoc-Implementierungen zu vermeiden. Z.B. Vermeiden Sie Typen a-la UserList - List<User> ist in den meisten Fällen gut genug.
  3. Folgen Vorschläge von Punkt 2. Darüber hinaus versuchen, so viel Schnittstellen zu Ihren Datentypen (Klassen), wie es möglich hinzuzufügen. Zum Beispiel müssen, wenn Sie wirklich UserList (zum Beispiel, wenn es eine Menge zusätzlicher Funktionalität haben soll) haben, fügen beide List und Iterable Schnittstellen zu seiner Definition.
  4. OOP (zumindest in Java und C#) ist für dieses Prinzip nicht sehr gut geeignet, weil sie versuchen, das ganze Objekt, das Verhalten während der anfänglichen Design zu kapseln, so dass es schwierig wird, mehr Funktionen ergänzen. In den meisten Fällen können Sie die fragliche Klasse erweitern und die benötigten Methoden in ein neues Objekt einfügen, aber 1) wenn jemand anders ihre eigene abgeleitete Klasse implementiert, ist sie nicht kompatibel mit Ihrer; 2) manchmal Klassen sind final oder alle Felder private gemacht, so abgeleiteten Klassen haben keinen Zugang zu ihnen (beispielsweise neue Funktionen Klasse hinzuzufügen String sollte implementieren zusätzliche Klasse StringUtils). Dennoch machen die oben beschriebenen Regeln die Verwendung von MFUFA im OOP-Code wesentlich einfacher. Und das beste Beispiel ist Clojure selbst, das anmutig im OO-Stil implementiert ist, aber immer noch dem MFUFA-Prinzip folgt.

UPD. Ich erinnere mich an eine andere Beschreibung des Unterschieds zwischen objektorientierten und funktionalen Stilen, die vielleicht besser alles zusammenfasst, was ich oben gesagt habe: Programm in OO-Stil zu entwerfen ist Denken in Bezug auf Datentypen (Substantive), beim Entwerfen im funktionalen Stil ist Denken Geschäftsbedingungen (Verben). Sie können vergessen, dass einige Substantive ähnlich sind (z. B. Vererbung vergessen), aber Sie sollten immer daran denken, dass viele Verben in der Praxis das Gleiche tun (z. B. gleiche oder ähnliche Schnittstellen haben).

+0

+1 Ich würde argumentieren, dass ein korrekter Einsatz von OO auch die Verben in den Mittelpunkt stellt - im Kern ist OO eine Art, polymorphe Funktionen bestimmter Datenbündel zu definieren. – Marcin

+1

Ich denke, es ist umgekehrt: OO ist über Verben (imperative Methoden, aufgerufen, um Dinge zu tun), während FP über Substantive (deklarative Funktionen, bezeichnet etwas) ist. Zum Beispiel haben Sie in OO oft Methoden der Form 'x.getSomething() ', instruieren zu" * bekommen * das Etwas von 'x'". In FP hast du stattdessen eine Funktion 'something (x)', die "* das * Etwas von' x' "bedeutet. –

+1

@ Marcin, @ AndreasRossberg: Ich stimme euch beiden zu. Darüber hinaus sagt GoF, dass Objekte in Bezug auf Operationen definiert werden sollten, die sie ausführen können, und nicht auf ihre interne Struktur (genau wie Schnittstellen). In diesem speziellen Fall, in dem wir FP und OOP vergleichen, würde ich jedoch sagen, dass Sie in OOP zunächst auf Entitäten im Domainbereich (Benutzer, Autos, Verträge) achten sollten, während Sie in FP zunächst an Aktionen in der Domäne denken sollten (verkaufen kaufen). Manchmal führt es zu verschiedenen Designs. Z.B. In FP können Sie 2 Module zum Verkaufen und Kaufen haben und alle Entitäten als Hash-Karten darstellen. – ffriend