Idiomatische Methode zur Implementierung von "Services" in F #

8

Wenn Sie in F # arbeiten und ein "Service" -Muster implementieren, z. B. Wrapper um Web-APIs oder Datenbanken oder USB-Gadgets, was ist der idiomatische Weg dazu? Meine Neigung ist, z.B. IDatabase und Database , ITwitter und Twitter , ICamera und Camera Schnittstellen und Klassen wie in C #, was einfache Test-Mocks ermöglicht. Aber ich möchte nicht "C # in F #" programmieren, wenn das nicht die Standardmethode ist.

Ich überlegte, DUs zu verwenden, um z.B. Camera | TestCamera , aber das bedeutet, dass der gesamte Code in die Codebasis der Produktion eingefügt wird, was sich schrecklich anhört. Aber vielleicht spricht das nur mein OO-Hintergrund.

Ich habe auch darüber nachgedacht, IDatabase zu einem Funktionsrekord zu machen. Ich bin immer noch offen für diese Idee, aber es scheint ein bisschen erfunden. Außerdem schließt es die Idee aus, jemals einen IoC-Controller oder irgendeine "MEF-ähnliche" Plugin-Fähigkeit zu verwenden (zumindest soweit ich weiß).

Was ist der "idiomatische F #" Weg dies zu tun? Folgen Sie einfach dem C # -Dienstmuster?

    
lobsterism 14.09.2015, 17:24
quelle

3 Antworten

4

Es ist nichts falsch an der Verwendung von Schnittstellen in F #.

Der Lehrbuch-FP-Ansatz würde bedeuten, dass der Client Funktionen übernimmt, die die Logik Ihrer Komponente als Argumente implementieren, aber dieser Ansatz skaliert nicht gut, da die Anzahl dieser Funktionen wächst. Eine Gruppierung ist eine gute Idee, da sie die Lesbarkeit verbessert und die Verwendung von Schnittstellen dafür eine idiomatische Methode in .NET ist. Es macht vor allem auf der Grenze der wohldefinierten Komponenten Sinn, und ich denke, dass die drei Schnittstellen, die Sie anführen, diese Beschreibung gut passen.

Ich habe gesehen, dass DU's ungefähr so ​​verwendet wird, wie du es beschrieben hast (sowohl die Produktion als auch eine falsche Implementierung), aber es passt auch nicht gut zu mir.

Wenn überhaupt, sind Funktionsaufzeichnungen nicht idiomatisch. Sie sind ein armer Mann, der das Verhalten in FP-Sprachen zusammenfasst, aber wenn es eine Sache gibt, die objektorientierte Sprachen richtig machen, bündelt sie Verhalten. Schnittstellen sind nur ein besseres Werkzeug für den Job, zumal F # es ermöglicht, Implementierungen inline mit der Syntax von Objektausdrücken zu erstellen.

    
scrwtp 14.09.2015, 18:45
quelle
9

Wie in der anderen Antwort erwähnt, ist die Verwendung von Schnittstellen in F # in Ordnung, und das könnte ein guter Weg sein, das Problem zu lösen. Allerdings, wenn Sie mehr funktionale Transformation-orientierten Stil der Programmierung verwenden, brauchen Sie sie möglicherweise nicht.

Idealerweise sollte der eigentliche interessante Code Transformationen sein, die keine Effekte ausführen - und daher keine Dienste aufrufen. Stattdessen nehmen sie nur Eingaben und produzieren Ausgaben. Lassen Sie mich anhand einer Datenbank demonstrieren.

Unter Verwendung eines IDatabase -Dienstes könnten Sie etwas wie folgt schreiben:

%Vor%

Wenn Sie so geschrieben werden, können Sie eine Pseudo-Implementierung von IDatabase bereitstellen, um zu testen, ob die Mittelungslogik in readAveragePrice korrekt ist. Sie können den Code jedoch auch so schreiben:

%Vor%

Jetzt können Sie calculateAveragePrice ohne jede Verspottung testen - geben Sie einfach einige Beispielprodukte, die Sie verarbeiten möchten! Dies verschiebt den Datenzugriff von der Kernlogik nach außen. Du hättest also:

%Vor%

Natürlich ist das ein simples Beispiel, aber es zeigt die Idee:

  

Schieben Sie den Datenzugriffscode außerhalb der Kernlogik und behalten Sie die Kernlogik als reine Funktionen bei, die Transformationen implementieren. Dann wird Ihre Kernlogik einfach zu testen sein - bei Beispieleingaben sollten sie die richtigen Ergebnisse liefern!

    
Tomas Petricek 14.09.2015 22:27
quelle
6

Während andere hier schreiben, dass an der Verwendung von Schnittstellen in F # nichts falsch ist, betrachte ich Schnittstellen als nichts anderes als einen Interop-Mechanismus, der es F # ermöglicht, mit in den anderen .NET-Sprachen geschriebenen Code zu arbeiten.

Wenn ich Code in C # schreibe, folge ich den SOLID-Prinzipien . Wenn es zusammen verwendet wird, das Prinzip der einfachen Verantwortung und das Interface Segregation Principle sollte Sie schließlich zu fahren so klein wie möglich zu definieren .

Daher sollten selbst in C # richtig gestaltete Schnittstellen nur eine einzige Methode haben :

%Vor%

Darüber hinaus impliziert das Prinzip der Abhängigkeitsinversion , dass "die abstrakten [...] Schnittstellen die Kunden besitzen" ( APPP , Kapitel 11), was bedeutet, dass künstliche Methodenbündel sowieso ein schlechtes Design sind .

Der einzige Grund, ein Interface wie das obige IProductQuery zu definieren, ist, dass in C # Schnittstellen immer noch der beste Mechanismus für Polymorphismus sind.

Andererseits gibt es in F # keinen Grund, eine Ein-Element-Schnittstelle zu definieren. Verwenden Sie stattdessen eine Funktion .

Sie können Funktionen als Argumente an andere Funktionen übergeben:

%Vor%

In diesem Beispiel werden Sie feststellen, dass getProducts als Argument an die Funktion calculateAveragePrice übergeben wird und ihr Typ nicht einmal deklariert wird. Dies passt wunderbar zu dem Prinzip, den Client die Schnittstelle definieren zu lassen: Der Typ des Arguments wird aus der Verwendung des Clients abgeleitet . Es hat den Typ 'a -> #seq<Product> (weil categoryId ein generischer Typ 'a sein kann).

Eine solche Funktion als calculateAveragePrice ist eine Funktion höherer Ordnung , die eine funktionale Aufgabe ist .

    
Mark Seemann 15.09.2015 06:31
quelle

Tags und Links