Wie wird Code in einer C # -Funktionsbibliothek ordnungsgemäß partitioniert?

8

Als eine der Hauptunterschiede des FP-Designs zu wiederverwendbaren Bibliotheken (für das, was ich lerne) ist, dass diese datenzentrischer sind als die entsprechenden OO (im Allgemeinen).

Dies scheint auch durch aufkommende Techniken wie TFD (Type-First-Development) bestätigt zu sein, die von Tomas Petricek in diese Blogpost.

Heutzutage ist die Sprache ein Multi-Paradigma und der gleiche Petricek erklärt in seinem Buch verschiedene funktionale Techniken, die von C # verwendet werden können.

Was mich hier interessiert, und daher die Frage, ist, wie man Code richtig partitioniert.

Also habe ich Bibliotheksdatenstrukturen definiert, die das Äquivalent diskriminierter Vereinigungen verwenden (wie in Petriceks Buch gezeigt), und ich projiziere, sie mit unveränderlichen Listen und / oder Tupeln zu verwenden, entsprechend der Domänenlogik meiner Anforderungen .

Wo platziere ich Operationen (Methoden ... Funktionen), die auf Datenstrukturen wirken?

Wenn ich eine Funktion höherer Ordnung definieren möchte, die einen Funktionswert verwendet, der in einem Standarddelegaten Func<T1...TResult> enthalten ist, wo platziere ich es?

Der gesunde Menschenverstand sagt mir, dass ich diese Methoden in statischen Klassen gruppieren soll, aber ich möchte eine Bestätigung von Leuten erhalten, die bereits funktionale Bibliotheken in C # geschrieben haben.

Angenommen, das ist richtig und ich habe eine Funktion wie folgt:

%Vor%

Wenn die Auswahl eines Wirbeltieres bestimmte N-Fälle enthält, die ich in der Bibliothek ausstellen möchte, was ist der richtige Weg, sie freizulegen?

%Vor%

oder

%Vor%

Jede Angabe wird sehr geschätzt.

BEARBEITEN:

Ich möchte zwei Sätze aus T. Petricek, Real World Functional Programming Vorwort von Mads Torgersen zitieren:

  

[...] Sie können funktionale Programmiertechniken in C # sehr gut nutzen,   obwohl es einfacher und natürlicher ist, dies in F # zu tun.   [...]   Funktionale Programmierung ist ein Geisteszustand. [...]

EDIT-2:

  • Ich glaube, dass es notwendig ist, die Frage weiter zu klären. Das im Titel erwähnte funktional bezieht sich ausschließlich auf Funktionale Programmierung ; Ich frage nicht die mehr funktionale Art der Gruppierung von Methoden, im Sinne von mehr Logik oder die Art und Weise, die im Allgemeinen mehr Sinn machen.

  • Dies bedeutet, dass die Implementierung versuchen wird, so weit wie möglich die Gründungskonzepte von FP zu verfolgen, die durch das NOOO Manifest zusammengefasst und hier zitiert werden Bequemlichkeit und Klarheit:

  
  1. Funktionen und Typen über Klassen
  2.   
  3. Reinheit gegenüber Veränderlichkeit
  4.   
  5. Zusammensetzung über Vererbung
  6.   
  7. Funktionen höherer Ordnung über Methodenversand
  8.   
  9. Optionen für Nullen
  10.   

Die Frage ist um wie man eine C # Bibliothek erstellt, die nach FP Konzepten geschrieben wurde, also (zum Beispiel) ist es absolut keine Option Methoden in die Datenstruktur zu setzen; weil das ein objektorientiertes Paradigma ist.

EDIT-3:

Auch wenn die Frage beantwortet wird (und verschiedene Kommentare), möchte ich nicht den falschen Eindruck erwecken, dass man gesagt hat, dass ein Programmierparadigma einem anderen überlegen ist. Wie zuvor werde ich in seinem Buch Expert F # 3.0 (Kap.20 - Entwerfen von F # -Bibliotheken - S. 565) eine Autorität über FP, Don Syme, erwähnen:

  

[...] Es ist ein weit verbreitetes Missverständnis, dass die funktionalen und OO-Programmiermethoden miteinander konkurrieren. In der Tat sind sie weitgehend orthogonal. [...]

    
jay 28.03.2013, 19:59
quelle

2 Antworten

3
  

Hinweis: Wenn Sie eine kürzere, genauere Antwort wünschen, lesen Sie meine andere Antwort. Ich bin mir bewusst, dass dieser hier scheint zu streunen scheint & amp; weiter für immer & amp; rede über dein Problem, aber vielleicht gibt es dir ein paar Ideen.

Es ist schwierig, Ihre Frage zu beantworten, ohne die genaue Beziehung zwischen Animal und Skeleton zu kennen. Ich werde eine Empfehlung über diese Beziehung in der zweiten Hälfte meiner Antwort geben, aber bevor ich das tue, werde ich einfach mit dem, was ich in Ihrem Beitrag sehe, einverstanden sein.

Zuerst werde ich versuchen, einige Dinge aus Ihrem Code abzuleiten:

%Vor%
  1. Wenn Sie diese Funktion nach funktionalen Prinzipien entworfen haben, sollte sie keine Nebenwirkungen haben. Das heißt, seine Ausgabe beruht nur auf ihren Argumenten. (Und in einer semi-objektorientierten Einstellung vielleicht auf andere statische Mitglieder von AnimalTopology ; aber da du keine gezeigt hast, lass uns diese Möglichkeit ignorieren.)

  2. Wenn die Funktion tatsächlich seiteneffektefrei ist (und nicht auf statische Mitglieder von AnimalTopology zugreift), dann legt die Typensignatur der Funktion nahe, dass es möglich ist, Animal von Skeleton abzuleiten. , weil es etwas annimmt, das auf Skeleton s wirkt und Animal s zurückgibt.

  3. Wenn das auch zutrifft, dann soll ich folgendes annehmen, um eine Antwort geben zu können:

    %Vor%

Nun ist es offensichtlich, dass Ihre Funktion nicht implementiert werden kann, da sie Animal s von Skeleton s ableiten könnte, aber nicht Skeleton . Es erhält nur eine Prädikatfunktion, die auf ein Skeleton wirkt. (Sie könnten dies beheben, indem Sie einen zweiten Parameter vom Typ Func<IEnumerable<Skeleton>> getSkeletons hinzufügen, aber ...)

Meiner Meinung nach würde etwas wie das folgende sinnvoller sein:

%Vor%

Nun, man könnte sich fragen, warum Sie Tiere von ihren Skeletten raten; und ist die bool Eigenschaft "ist Wirbeltier" eine inhärente Eigenschaft eines Tieres (oder Skeletts)? Gibt es wirklich mehrere Möglichkeiten, darüber zu entscheiden?

Ich würde Folgendes vorschlagen:

%Vor%

Bitte beachten Sie die Verwendung der obigen Erweiterungsmethoden. Hier ist ein Beispiel, wie man es benutzt:

%Vor%

Nehmen wir nun an, dass Ihre Erweiterungsmethode komplexere Arbeit geleistet hat. In diesem Fall ist es vielleicht eine gute Idee, sie in ihren eigenen "Algorithmus-Typ" zu setzen:

%Vor%

Dies hat den Vorteil, dass es z. über einen Klassenkonstruktor; und Sie könnten den Algorithmus in mehrere Methoden aufteilen, die alle in derselben Klasse liegen (aber alle sind private außer für GetVertebrates .)

Natürlich können Sie die gleiche Art der Parametrisierung mit funktionalen Schließungen durchführen, aber meiner Erfahrung nach wird das in einer C # -Einstellung schnell unordentlich. Hier sind Klassen ein gutes Mittel, um eine Gruppe von Funktionen zu einer logischen Einheit zusammenzufassen.

    
stakx 29.03.2013, 10:01
quelle
2
  

Wo platziere ich Operationen (Methoden ... Funktionen), die auf Datenstrukturen wirken?

Ich sehe vier allgemeine Ansätze (in keiner bestimmten Reihenfolge):

  • Fügen Sie die Funktionen in die Datenstrukturen ein. (Dies ist der objektorientierte "Methode" -Ansatz. Er ist geeignet, wenn eine Funktion nur auf eine Instanz dieses Typs wirkt. Sie ist vielleicht weniger geeignet, zB wenn eine Funktion mehrere Objekte von "zusammenzieht" verschiedene Arten, und spuckt ein Objekt von einem anderen Typ aus.In diesem Fall würde ich ...)

  • Fügen Sie die Funktionen in ihre eigenen "Algorithmusklassen" ein. (Dies erscheint sinnvoll, wenn die Funktionen viel oder komplex arbeiten oder parametrisiert / konfiguriert werden müssen oder wenn Sie den Algorithmus in mehrere Funktionen aufteilen möchten, die Sie logisch gruppieren können, indem Sie sie in einen Klassentyp einfügen. )

  • Verwandle die Funktionen in lambdas (anonyme Delegierte, Schließungen usw.). (Dies funktioniert gut, wenn sie klein sind und nur an einem bestimmten Ort benötigt werden; der Code kann nicht einfach an einem anderen Ort wiederverwendet werden.)

  • Fügen Sie die Funktionen in eine statische Klasse ein und machen Sie sie zu Erweiterungsmethoden . (So ​​funktioniert LINQ to Objects. Es ist ein hybrider, funktionaler und objektorientierter Ansatz. Es erfordert einige besondere Sorgfalt, um das Problem der Entdeckbarkeit / Namespacing richtig zu stellen. Viele Leute werden denken, dass dieser Ansatz die "Kapselung" durchbricht ein Gegenargument, lesen Sie den ausgezeichneten C ++ - Artikel "Wie Nicht-Mitgliedsfunktionen die Kapselung verbessern "; ersetze" Erweiterungsmethode "für" Nicht-Mitglied-Freund-Funktion ".)

  

Hinweis: Ich könnte detaillierter auf diese Punkte eingehen, wenn die Leute es wollen, aber bevor ich das tue, werde ich abwarten, welche Art von Feedback diese Antwort erhält.

    
stakx 29.03.2013 10:10
quelle