Hallo allerseits! Es ist für niemanden ein Geheimnis, dass es in der Programmierwelt viele Techniken, Praktiken und Programmiermuster (Design) gibt, aber wenn man etwas Neues lernt, ist oft nicht klar, wo und wie man dieses Neue anwendet.
Anhand des Beispiels der Erstellung eines kleinen Wrapper-Moduls für die Arbeit mit http-Anforderungen werden wir heute die tatsächlichen Vorteile des Curry analysieren - den Empfang der funktionalen Programmierung.
An alle Neulinge und diejenigen, die daran interessiert sind, funktionale Programmierung in der Praxis einzusetzen - willkommen, diejenigen, die genau verstehen, was Currying ist -, freue ich mich auf Ihre Kommentare zum Code, denn wie sie sagen - der Perfektion sind keine Grenzen gesetzt.
Also fangen wir an
Aber nicht aus dem Konzept des Currying, sondern aus der Aussage des Problems, wo wir es anwenden können.
Wir haben eine bestimmte Blog-API, die nach dem folgenden Prinzip arbeitet (alle Übereinstimmungen mit echten APIs sind ein Unfall):
- Eine Anfrage an
/api/v1/index/
gibt Daten für die Hauptseite zurück - Eine Anfrage an
/api/v1/news/
gibt Daten für die Nachrichtenseite zurück - Eine Anfrage an
/api/v1/articles/
gibt Daten für die Artikelliste zurück - Die Anforderung an
/api/v1/article/222/
gibt die Artikelseite mit der ID 222 zurück - Eine Anfrage an
/api/v1/article/edit/222/
gibt ein Artikelbearbeitungsformular mit der ID 222 zurück
... und so weiter, weiter, weiter
Wie Sie sehen können, müssen wir uns für den Zugriff auf die API der API einer bestimmten Version v1 zuwenden (wie wenig sie wächst und eine neue Version veröffentlicht wird) und dann die Datenanforderung weiter entwerfen.
Um in js-Code Daten zu erhalten, z. B. einen Artikel mit der ID 222, müssen wir daher schreiben (um das Beispiel so weit wie möglich zu vereinfachen, verwenden wir die native js-Abrufmethode):
fetch('/api/v1/article/222/') .then() .catch()
Um denselben Artikel zu bearbeiten, fordern wir Folgendes an:
fetch('/api/v1/article/edit/222/') .then() .catch()
Sicher haben Sie bereits bemerkt, dass es in unseren Anfragen viele doppelte Pfade gibt. Zum Beispiel den Pfad und die Version zu unserer API /api/v1/
und die Arbeit mit einem Artikel /api/v1/article/
und /api/v1/article/edit/
.
Wie kann der API-Anforderungscode gemäß unserer bevorzugten DRY-Regel (Don't Repeat Yourself) optimiert werden?
Wir können Konstanten Abfrageteile hinzufügen, zum Beispiel:
const API = '/api' const VERSION = '/v1' const ARTICLE = `${API}${VERSION}/article`
Und jetzt können wir die obigen Beispiele folgendermaßen umschreiben:
Artikelanfrage
fetch(`${ARTICLE}/222/`)
Artikelbearbeitungsanforderung
fetch(`${ARTICLE}/edit/222/`)
Der Code scheint weniger zu sein, es gibt Konstanten im Zusammenhang mit der API, aber Sie und ich wissen, was viel bequemer gemacht werden kann.
Ich glaube, dass es noch Möglichkeiten gibt, das Problem zu lösen, aber unsere Aufgabe ist es, die Lösung mithilfe von Currying zu prüfen.
Das Prinzip der Erstellung von Anforderungen basierend auf http-Diensten
Die Strategie besteht darin, eine bestimmte Funktion zu erstellen, indem wir API-Anforderungen aufrufen.
Wie es funktionieren soll
Wir konstruieren die Anforderung, indem wir die Wrapper-Funktion über den nativen Abruf aufrufen (nennen wir sie http. Unten finden Sie den vollständigen Code für diese Funktion), in deren Argumenten wir die Anforderungsparameter übergeben:
cosnt httpToArticleId222 = http({ url: '/api/v1/article/222/', method: 'POST' })
Bitte beachten Sie, dass das Ergebnis dieser http-Funktion eine Funktion ist, die die Einstellungen für URL und Methodenanforderung enthält.
Durch Aufrufen von httpToArticleId222()
senden wir die Anforderung nun tatsächlich an die API.
Sie können schwierigere und schrittweise Designabfragen durchführen. Auf diese Weise können wir eine Reihe vorgefertigter Funktionen mit verkabelten API-Pfaden erstellen. Wir werden sie http-Dienste nennen.
Zunächst erstellen wir einen API-Aufrufdienst (und fügen gleichzeitig Anforderungsparameter hinzu, die für alle nachfolgenden Anforderungen unverändert bleiben, z. B. eine Methode).
const httpAPI = http({ url: '/api', method: 'POST' })
Jetzt erstellen wir den Dienst für den Zugriff auf die API der ersten Version. In Zukunft können wir einen separaten Anforderungszweig vom httpAPI-Dienst zu einer anderen Version der API erstellen.
const httpAPIv1 = httpAPI({ url: '/v1' })
Der Dienst für den Zugriff auf die API der ersten Version ist bereit. Jetzt erstellen wir daraus Dienste für den Rest der Daten (denken Sie an die improvisierte Liste am Anfang des Artikels).
Homepage-Daten
const httpAPIv1Main = httpAPIv1({ url: '/index' })
News Page Daten
const httpAPIv1News = httpAPIv1({ url: '/news' })
Artikellistendaten
const httpAPIv1Articles = httpAPIv1({ url: '/articles' })
Schließlich kommen wir zu unserem Hauptbeispiel, den Daten für das Material
const httpAPIv1Article = httpAPIv1({ url: '/article' })
Wie erhalte ich den Weg zur Artikelbearbeitung? Sie haben es erraten, wir laden natürlich Daten aus der zuvor erstellten Funktion httpAPIv1Article
const httpAPIv1ArticleEdit = httpAPIv1({ url: '/edit' })
Ein kleines logisches Ergebnis
Wir haben also eine schöne Liste von Diensten, die sich zum Beispiel in einer separaten Datei befinden, was uns überhaupt nicht stört. Wenn etwas in der Anfrage geändert werden muss, weiß ich genau, wo ich es bearbeiten muss.
export { httpAPIv1Main, httpAPIv1News, httpAPIv1Articles, httpAPIv1Article, httpAPIv1ArticleEdit }
Ich importiere einen Dienst mit einer bestimmten Funktion
import { httpAPIv1Article } from 'services'
Und ich führe die Anfrage aus, rekonstruiere sie zuerst, indem ich die ID des Materials hinzufüge, und rufe dann die Funktion auf, um die Anfrage zu senden (wie sie sagen: "einfach")
httpAPIv1Article({ url: ArticleID // id - })() .then() .catch()
Sauber, schön, verständlich (keine Werbung)
Wie funktioniert es?
Wir können eine Funktion mit Daten genau aufgrund von Currying „laden“.
Ein bisschen Theorie.
Currying ist eine Möglichkeit, eine Funktion mit der Fähigkeit zu konstruieren, ihre Argumente schrittweise anzuwenden. Dies wird erreicht, indem die Funktion nach dem Aufruf zurückgegeben wird.
Ein klassisches Beispiel ist die Hinzufügung.
Wir haben eine Summenfunktion. Beim ersten Aufruf übergeben wir die erste Nummer für das anschließende Falten. Nach dem Aufruf erhalten wir eine neue Funktion, die erwartet, dass eine zweite Zahl die Summe berechnet. Hier ist ihr Code (ES6-Syntax)
const sum = a => b => a + b
Wir nennen es das erste Mal (Teilanwendung) und speichern das Ergebnis in einer Variablen, zum Beispiel sum13
const sum13 = sum(13)
Jetzt können wir sum13 auch mit der fehlenden Nummer im Argument aufrufen, deren Ergebnis 13 + das zweite Argument ist
sum13(7)
Wie kann man das auf unsere Aufgabe anwenden?
Wir erstellen die http- Funktion, die der Wrapper über Fetch sein wird
function http (paramUser) {}
Dabei sind paramUser Anforderungsparameter, die zum Zeitpunkt des Funktionsaufrufs übergeben wurden
Beginnen wir damit, unserer Funktion Logik hinzuzufügen.
Standardmäßig angeforderte Anforderungsparameter hinzufügen.
function http (paramUser) { /** * -, * @type {string} */ let param = { method: 'GET', credentials: 'same-origin' } }
Und dann die paramGen- Funktion, die Anforderungsparameter aus den standardmäßig festgelegten und benutzerdefinierten Parametern generiert (tatsächlich nur eine Zusammenführung von zwei Objekten).
function http (paramUser) { /** * -, * @type {string} */ let param = { method: 'GET', credentials: 'same-origin' } /** * , * url , * * @param {object} param * @param {object} paramUser , * * @return {object} */ function paramGen (param, paramUser) { let url = param.url || '' let newParam = Object.assign({}, param, paramUser) url += paramUser.url || '' newParam.url = url return newParam } }
Wir gehen zum Wichtigsten über, wir beschreiben Curry
Die Funktion, die zum Beispiel Fabric aufgerufen und von der http- Funktion zurückgegeben wird, hilft uns dabei.
function http (paramUser) { /** * -, * @type {string} */ let param = { method: 'GET', credentials: 'same-origin' } /** * , * url , * * @param {object} param * @param {object} paramUser , * * @return {object} */ function paramGen (param, paramUser) { let url = param.url || '' url += paramUser.url || '' let newParam = Object.assign({}, param, paramUser); newParam.url = url return newParam } /** * , * , * * : * * - , , * - , * - , * * @param {object} param , * @param {object} paramUser , * * @return {function || promise} , (fetch), */ function fabric (param, paramUser) { if (paramUser) { if (typeof paramUser === 'string') { return fabric.bind(null, paramGen(param, { url: paramUser })) } return fabric.bind(null, paramGen(param, paramUser)) } else { // , , param url, // :) return fetch(param.url, param) } } return fabric.bind(null, paramGen(param, paramUser)) }
Der erste Aufruf der http- Funktion gibt die Fabric- Funktion zurück, an die Parameterparameter übergeben (und von der paramGen- Funktion konfiguriert) werden, die auf ihre Funktion warten Stunden später anrufen.
Konfigurieren Sie beispielsweise die Anforderung
let httpGift = http({ url: '
Beim Aufruf von httpGift werden die übergebenen Parameter angewendet. Als Ergebnis geben wir fetch zurück . Wenn wir die Anforderung neu konfigurieren möchten, übergeben wir die neuen Parameter einfach an die generierte httpGift- Funktion und erwarten, dass sie ohne Argumente aufgerufen wird
httpGift() .then() .catch()
Zusammenfassung
Dank des Einsatzes von Curry bei der Entwicklung verschiedener Module können wir eine hohe Flexibilität bei der Verwendung von Modulen und eine einfache Prüfung erreichen. Wie zum Beispiel beim Organisieren der Architektur von Diensten für die Arbeit mit der API.
Es ist, als würden wir eine Minibibliothek erstellen, mit deren Werkzeugen wir eine einzige Infrastruktur für unsere Anwendung erstellen.
Ich hoffe die Informationen waren nützlich, nicht hart treffen, dies ist mein erster Artikel in meinem Leben :)
Alles kompilierte Code, bis bald!