Betrachten Sie das folgende einfache Beispiel.
%Vor% Wie Sie sehen können, wird dieselbe Mustervergleichslogik in zwei Funktionen wiederholt. Wenn ich OOP verwenden würde, würde ich die Schnittstelle IPaymentInstrument
erstellen und zwei Operationen definieren:
PrintInstrumentName
und PrintRequisites
und dann Klassen implementieren - eine pro Zahlungsinstrument. Um das Instrument in Abhängigkeit von einigen externen Bedingungen zu instanziieren, würde ich (zum Beispiel) das Fabrikmuster ( PaymentInstrumentFactory
) verwenden.
Wenn ich ein neues Zahlungsinstrument hinzufügen müsste, muss ich nur eine neue Klasse hinzufügen, die IPaymentInstrument
interface implementiert und die Factory Instanziierungslogik aktualisiert. Anderer Code, der diese Klassen verwendet, bleibt unverändert.
Aber wenn ich den funktionalen Ansatz verwende, sollte ich jede -Funktion aktualisieren, bei der Mustererkennung für diesen Typ existiert.
Wenn es viele Funktionen gibt, die PaymentInstrument
type verwenden, ist das ein Problem.
Wie kann dieses Problem mit einem funktionalen Ansatz behoben werden?
Mit Mark Seemanns Antwort kam ich zu einer solchen Designentscheidung.
%Vor%Und Verwendung
%Vor% Wie Sie sehen können, führt die Funktion getTypeOperations
die Rolle des Fabrikmusters aus. Um Funktionen in einem Bündel zu aggregieren, benutze ich einen einfachen Datensatztyp (jedoch sind gemäß den F # Designrichtlinien Ссылка Schnittstellen vorhanden Vorgezogen zu einer solchen Entscheidung, aber ich bin daran interessiert, es in funktionalen Ansatz zu tun, um es jetzt besser zu verstehen).
Ich habe bekommen, was ich wollte - Pattern Matching gibt es nur an einer Stelle.
Betrachten Sie das folgende einfache Beispiel.
%Vor% Wie Sie sehen können, wird dieselbe Mustervergleichslogik in zwei Funktionen wiederholt. Wenn ich OOP verwenden würde, würde ich die Schnittstelle Check
erstellen und zwei Operationen definieren:
CreditCard
und unit -> unit
und dann Klassen implementieren - eine pro Zahlungsinstrument. Um das Instrument in Abhängigkeit von einigen externen Bedingungen zu instanziieren, würde ich (zum Beispiel) das Fabrikmuster ( printInstrumentName
) verwenden.
Wenn ich ein neues Zahlungsinstrument hinzufügen müsste, muss ich nur eine neue Klasse hinzufügen, die printRequisites
interface implementiert und die Factory Instanziierungslogik aktualisiert. Anderer Code, der diese Klassen verwendet, bleibt unverändert.
Aber wenn ich den funktionalen Ansatz verwende, sollte ich jede -Funktion aktualisieren, bei der Mustererkennung für diesen Typ existiert.
Wenn es viele Funktionen gibt, die %code% type verwenden, ist das ein Problem.
Wie kann dieses Problem mit einem funktionalen Ansatz behoben werden?
Wie Patryk Ćwiek in dem obigen Kommentar darauf hinweist, begegnen Sie dem Ausdruck Problem , so dass Sie die eine oder andere wählen müssen.
Wenn die Möglichkeit, weitere Datentypen hinzuzufügen, für Sie wichtiger ist als die Möglichkeit, einfach mehr Verhalten hinzuzufügen, ist ein schnittstellenbasierter Ansatz möglicherweise geeigneter.
In F # können Sie noch objektorientierte Schnittstellen definieren:
%Vor%Sie können auch Klassen erstellen, die diese Schnittstelle implementieren. Hier ist %code% , und ich werde %code% als Übung für den Leser hinterlassen:
%Vor% Wenn Sie jedoch den objektorientierten Weg gehen wollen, sollten Sie die SOLID-Prinzipien
Sie können dies immer noch in Klassen implementieren:
%Vor%Das fängt jetzt an, etwas lächerlich zu wirken. Wenn Sie F # verwenden, dann warum sollten Sie alle Probleme bei der Definition einer Schnittstelle mit einem einzelnen Member aufgreifen?
Verwenden Sie stattdessen Funktionen ?
Beide gewünschten Interface-Member haben den Typ %code% (obwohl kein besonders "funktional" aussehender Typ), also warum nicht solche Funktionen herumreichen und auf den Interface-Overhead verzichten?
Mit den Funktionen %code% und %code% aus dem OP haben Sie bereits das gewünschte Verhalten. Wenn Sie sie in polymorphe 'Objekte' verwandeln wollen, die die gewünschte Schnittstelle 'implementieren', können Sie über sie schließen:
%Vor%In der funktionalen Programmierung nennen wir diese Dinge nicht Objekte , sondern closures . Statt Daten mit Verhalten zu sein, sind sie Verhalten mit Daten .
Mit Mark Seemanns Antwort kam ich zu einer solchen Designentscheidung.
%Vor%Und Verwendung
%Vor%Wie Sie sehen können, führt die Funktion %code% die Rolle des Fabrikmusters aus. Um Funktionen in einem Bündel zu aggregieren, benutze ich einen einfachen Datensatztyp (jedoch sind gemäß den F # Designrichtlinien Ссылка Schnittstellen vorhanden Vorgezogen zu einer solchen Entscheidung, aber ich bin daran interessiert, es in funktionalen Ansatz zu tun, um es jetzt besser zu verstehen).
Ich habe bekommen, was ich wollte - Pattern Matching gibt es nur an einer Stelle.
Tags und Links f# functional-programming