Beim neuen Programmierhype geht es um funktionale Programmierparadigmen. Funktionale Sprachen werden immer häufiger in größeren und besseren Anwendungen eingesetzt. Scala, Haskel usw. gedeihen und andere, konservativere Sprachen wie Java begannen, einige der funktionalen Programmierparadigmen zu übernehmen (siehe Abschlüsse in Java7 und Lazy Eval für Listen in Java8). Was jedoch nur wenige wissen, ist, dass PHP in Bezug auf die funktionale Programmierung sehr vielseitig ist. Alle wesentlichen funktionalen Programmierungskonzepte können in PHP ausgedrückt werden. Wenn Sie also mit der funktionalen Programmierung noch nicht vertraut sind, sollten Sie sich darauf vorbereiten, Ihre Gedanken durchzubringen, und wenn Sie bereits mit der funktionalen Programmierung vertraut sind, sollten Sie sich darauf einstellen, dass Sie mit diesem Tutorial großen Spaß haben werden.
Ohne Programmierparadigmen könnten wir alles tun, was wir wollen, wie auch immer wir wollen. Dies würde zwar zu extremer Flexibilität führen, aber auch zu unmöglichen Architekturen und sehr aufgeblähtem Code. Daher wurden Programmierparadigmen erfunden, um uns Programmierern dabei zu helfen, auf spezifische Weise über ein bestimmtes Programm nachzudenken und auf diese Weise unsere Fähigkeit einzuschränken, unsere Lösung auszudrücken.
Jedes Programmierparadigma nimmt uns die Freiheit:
Bei der funktionalen Programmierung haben Sie keine Daten, die durch Variablen dargestellt werden.
In der funktionalen Programmierung alles ist eine funktion. Und ich meine alles. Beispielsweise kann ein Satz wie in der Mathematik als mehrere Funktionen dargestellt werden. Ein Array oder eine Liste ist auch eine Funktion oder eine Gruppe von Funktionen.
In der objektorientierten Programmierung ist alles ein Objekt. Ein Objekt ist eine Sammlung von Daten und Methoden, die mit diesen Daten Aktionen ausführen. Objekte haben einen Zustand, einen flüchtigen, veränderbaren Zustand.
Bei der funktionalen Programmierung haben Sie keine Daten, die durch Variablen dargestellt werden. Es gibt keine Datencontainer. Daten werden keiner Variablen zugewiesen. Einige Werte können definiert und zugewiesen werden. In den meisten Fällen handelt es sich jedoch um Funktionen, die "Variablen" zugewiesen sind. Ich habe "Variablen" zwischen Anführungszeichen gesetzt, weil sie in der funktionalen Programmierung sind unveränderlich. Obwohl die meisten funktionalen Programmiersprachen keine Unveränderlichkeit erzwingen, erzwingen die meisten objektorientierten Sprachen keine Objekte. Wenn Sie den Wert nach einer Zuweisung ändern, führen Sie keine rein funktionale Programmierung mehr durch.
Da Sie Variablen keine Werte zugewiesen haben, haben Sie in der funktionalen Programmierung entsprechende Werte kein Zustand.
Da keine Zustände und keine Zuordnungen vorhanden sind, bei der funktionalen Programmierung Funktionen haben keine Nebenwirkung. Und aus drei Gründen, Funktionen sind immer vorhersehbar. Das bedeutet im Wesentlichen, dass Sie immer, wenn Sie eine Funktion mit denselben Parametern immer wieder aufrufen, immer dasselbe Ergebnis haben. Dies ist ein großer Vorteil gegenüber der objektorientierten Programmierung und reduziert die Komplexität von Multithread- und Massive-Multithread-Anwendungen erheblich.
Wenn wir jedoch alles in Funktionen ausdrücken wollen, müssen wir sie Parametern zuweisen oder von anderen Funktionen zurückgeben können. Daher erfordert die funktionale Programmierung die Unterstützung von Funktionen höherer Ordnung. Dies bedeutet im Grunde, dass eine Funktion einer "Variablen" zugewiesen, als Parameter an eine andere Funktion gesendet und als Ergebnis einer Funktion zurückgegeben werden kann.
Schließlich, weil wir keine Werte in Variablen haben, sind while und for Schleifen für die funktionale Programmierung ungewöhnlich und werden durch Rekursion ersetzt.
Genug geredet und Philosophie für eine Stunde. Lassen Sie uns Code schreiben!
Richten Sie ein PHP-Projekt in Ihrer bevorzugten IDE oder im Code-Editor ein. Erstellen Sie darin ein "Tests"
Mappe. Erstellen Sie zwei Dateien: FunSets.php
im Ordner des Projekts und FunSetsTest.php
im Testordner. Wir erstellen eine Anwendung mit Tests, die das Konzept von Mengen darstellen.
In der Mathematik ist eine Menge eine Sammlung verschiedener Objekte, die als eigenständiges Objekt betrachtet werden. (Wikipedia)
Das bedeutet im Grunde, dass Sets eine Menge Dinge an einem einzigen Ort sind. Diese Mengen können und werden durch mathematische Operationen charakterisiert: Vereinigungen, Schnittpunkte, Unterschiede usw. Und durch umsetzbare Eigenschaften wie: enthält.
Also lasst uns Code schreiben! Aber warte. Wie? Nun, um die Konzepte der funktionalen Programmierung zu respektieren, müssen wir folgende Einschränkungen auf unseren Code anwenden:
Für Tests gelten keine Einschränkungen. Aufgrund der Natur von PHPUnit verwenden wir dort klassischen objektorientierten PHP-Code. Um unsere Tests besser unterzubringen, werden wir unseren gesamten Produktionscode in einer einzigen Klasse zusammenfassen.
Wenn Sie ein erfahrener Programmierer sind, aber mit der funktionalen Programmierung nicht vertraut sind, Jetzt ist es an der Zeit, nicht mehr wie üblich zu denken und seien Sie bereit, Ihre Komfortzone zu verlassen. Vergessen Sie alle Ihre bisherigen Denkweisen über ein Problem und stellen Sie sich alle Funktionen vor.
Die definierende Funktion einer Menge ist ihre "Enthält" -Methode.
Funktion enthält ($ set, $ elem) return $ set ($ elem);
OK… Das ist nicht so offensichtlich, also sehen wir uns an, wie wir es benutzen würden.
$ set = Funktion ($ Element) return true;; enthält ($ set, 100);
Nun, das erklärt es ein bisschen besser. Die Funktion "enthält"
hat zwei Parameter:
$ set
- repräsentiert eine Menge, die als Funktion definiert ist.$ elem
- repräsentiert ein Element, das als Wert definiert ist.In diesem Zusammenhang alles "enthält"
zu tun ist, die Funktion in anzuwenden $ set
mit dem Parameter $ elem
. Lassen Sie uns alle in einen Test einwickeln.
class FunSetsTest erweitert PHPUnit_Framework_TestCase private $ funSets; geschützte Funktion setUp () $ this-> funSets = new FunSets (); function testContainsIsImplemented () // Wir charakterisieren eine Menge durch ihre include-Funktion. Es ist die Grundfunktion eines Sets. $ set = Funktion ($ Element) return true;; $ this-> assertTrue ($ this-> funSets-> enthält ($ set, 100));
Und verpacken Sie unseren Produktionscode FunSets.php
in eine Klasse:
class FunSets public-Funktion enthält ($ set, $ elem) return $ set ($ elem);
Sie können diesen Test tatsächlich ausführen und er wird bestanden. Die Menge, die wir für diesen Test definiert haben, ist nur eine Funktion, die immer true zurückgibt. Es ist eine "wahre Menge".
Wenn das vorige Kapitel ein wenig verwirrend war oder in der Logik unbrauchbar aussah, wird es dies etwas klarer machen. Wir möchten ein Set mit einem einzelnen Element, einem Singleton-Set, definieren. Denken Sie daran, dass dies eine Funktion sein muss, und wir möchten sie wie im unten stehenden Test verwenden.
function testSingletonSetContainsSingleElement () // Eine Singleton-Gruppe, die von einer Funktion charakterisiert wird, an die übergeben wurde, gibt für das einzelne Element den Wert // zurück, der als Parameter übergeben wird. Mit anderen Worten, ein Singleton ist eine Menge mit einem einzelnen Element. $ singleton = $ this-> funSets-> singletonSet (1); $ this-> assertTrue ($ this-> funSets-> enthält ($ singleton, 1));
Wir müssen eine Funktion definieren, die aufgerufen wird "singeltonSet"
mit einem Parameter, der ein Element der Menge darstellt. Im Test ist das die Nummer Eins (1). Dann erwarten wir unsere enthält
Methode, wenn mit einer Singleton-Funktion aufgerufen, um zurückzukehren wahr
wenn der gesendete Parameter gleich eins ist. Der Code, der den Test bestanden hat, lautet wie folgt:
public function singletonSet ($ elem) return-Funktion ($ otherElem) use ($ elem) return $ elem == $ otherElem; ;
Beeindruckend! Das ist verrückt. Also die Funktion "singletonSet"
bekommt als Parameter ein Element als $ elem
. Dann wird eine andere Funktion zurückgegeben, die einen Parameter hat $ otherElem
und diese zweite Funktion wird vergleichen $ elem
zu $ otherElem
.
Wie funktioniert das? Zuerst diese Zeile:
$ singleton = $ this-> funSets-> singletonSet (1);
verwandelt sich in was "singletonSet (1)"
kehrt zurück:
$ singleton = Funktion ($ otherElem) return 1 == $ otherElem; ;
Dann "enthält ($ singleton, 1)"
wird genannt. Welches wiederum ruft was in ist $ singleton
. Der Code wird also:
$ singleton (1)
Mit welcher der Code tatsächlich ausgeführt wird $ otherElem
den Wert eins haben.
return 1 == 1
Was natürlich stimmt und unser Test bestanden hat.
Lächelst du schon Fühlen Sie, wie Ihr Gehirn anfängt zu kochen? Ich habe es sicherlich getan, als ich dieses Beispiel zum ersten Mal in Scala geschrieben habe, und ich habe es wieder getan, als ich dieses Beispiel zum ersten Mal in PHP geschrieben habe. Ich finde das außergewöhnlich. Es ist uns gelungen, eine Menge mit einem Element zu definieren, mit der Möglichkeit zu überprüfen, dass sie den Wert enthält, den wir in sie übergeben haben. Wir haben das alles ohne eine einzige Wertzuweisung gemacht. Wir haben keine Variable, die den Wert Eins enthält oder den Status Eins hat. Kein Zustand, keine Zuordnung, keine Veränderlichkeit, keine Schleifen. Wir sind hier auf dem richtigen Weg.
Da wir nun einen Satz mit einem einzigen Wert erstellen können, müssen wir einen Satz mit mehreren Werten erstellen können. Der naheliegende Weg besteht darin, die Gewerkschaftsoperation auf unseren Sets zu definieren. Die Vereinigung zweier Singleton-Sets repräsentiert eine andere Vereinigung mit beiden Werten. Ich möchte, dass Sie sich eine Minute Zeit nehmen und über die Lösung nachdenken, bevor Sie zum Code scrollen. Nehmen Sie vielleicht einen Blick auf die unten aufgeführten Tests.
function testUnionContainsAllElements () // Eine Union ist durch eine Funktion gekennzeichnet, die 2 Sets als Parameter abruft und alle bereitgestellten Sets enthält. // Wir können an dieser Stelle nur Singletons erstellen, also erstellen wir 2 Singletons und vereinigen sie mit $ s1 = $ this -> funSets-> singletonSet (1); $ s2 = $ this-> funSets-> singletonSet (2); $ union = $ this-> funSets-> union ($ s1, $ s2); // Überprüfen Sie nun, ob sowohl 1 als auch 2 Teil der Union sind. $ This-> assertTrue ($ this-> funSets-> enthält ($ union, 1)); $ this-> assertTrue ($ this-> funSets-> enthält ($ union, 2)); //… und dass es nicht 3 $ this-> assertFalse enthält ($ this-> funSets-> enthält ($ union, 3));
Wir wollen eine Funktion namens "Union"
das bekommt zwei Parameter, beide Sätze. Denken Sie daran, Sets sind nur Funktionen für uns, also unsere "Union"
Funktion erhält zwei Funktionen als Parameter. Dann wollen wir mit überprüfen können "enthält"
ob die Vereinigung ein Element enthält oder nicht. So unser "Union"
Funktion muss eine andere Funktion zurückgeben "enthält"
Kann benutzen.
public function union ($ s1, $ s2) return-Funktion ($ otherElem) use ($ s1, $ s2) return $ this-> enthält ($ s1, $ otherElem) || $ this-> enthält ($ s2, $ otherElem); ;
Das funktioniert eigentlich ganz gut. Und es ist vollkommen gültig, auch wenn Ihre Gewerkschaft zusammen mit einer anderen Gewerkschaft plus einem Einzelspieler aufgerufen wird. Es ruft enthält
in sich für jeden Parameter. Wenn es eine Gewerkschaft ist, wird es wiederkehren. So einfach ist das.
Wir können dieselbe Einzeilenlogik mit geringfügigen Änderungen anwenden, um die nächsten zwei wichtigsten Funktionen zu erhalten, die eine Menge charakterisieren: Schnittmenge - enthält nur die gemeinsamen Elemente zwischen zwei Mengen - und die Differenz - enthält nur die Elemente der ersten Menge, die nicht Teil sind vom zweiten Satz.
public function intersect ($ s1, $ s2) return-Funktion ($ otherElem) use ($ s1, $ s2) return $ this-> enthält ($ s1, $ otherElem) && $ this-> enthält ($ s2, $ otherElem); ; public function diff ($ s1, $ s2) return-Funktion ($ otherElem) use ($ s1, $ s2) return $ this-> enthält ($ s1, $ otherElem) &&! $ this-> enthält ($ s2 , $ otherElem); ;
Ich werde Sie nicht mit dem Testcode für diese beiden Methoden überfluten. Die Tests werden geschrieben und Sie können sie überprüfen, wenn Sie in dem beigefügten Code nachsehen.
Nun, das ist etwas komplizierter, wir können das nicht mit einer einzigen Codezeile lösen. Ein Filter ist eine Funktion, die zwei Parameter verwendet: eine Mengen- und eine Filterfunktion. Sie wendet die Filterfunktion auf eine Menge an und gibt eine andere Menge zurück, die nur die Elemente enthält, die die Filterfunktion erfüllen. Um es besser zu verstehen, hier ist der Test dafür.
function testFilterContainsOnlyElementsThatMatchConditionFunction () $ u12 = $ this-> createUnionWithElements (1, 2); $ u123 = $ this-> funSets-> union ($ u12, $ this-> funSets-> singletonSet (3)); // Filterregel, Elemente suchen, die größer als 1 sind (Bedeutung 2 und 3). $ Condition = function ($ elem) return $ elem> 1;; // Gefiltertes Set $ gefiltertSet = $ this-> funSets-> filter ($ u123, $ condition); // Vergewissern Sie sich, dass das gefilterte Set nicht 1 enthält. $ This-> assertFalse ($ this-> funSets-> enthält ($ gefiltertesSet, 1), "Sollte nicht 1 enthalten"); // Überprüfen Sie, ob es 2 und 3 enthält. $ This-> assertTrue ($ this-> funSets-> enthält ($ gefiltertesSet, 2), "Sollte 2 enthalten"); $ this-> assertTrue ($ this-> funSets-> enthält ($ gefiltertesSet, 3), "Sollte 3 enthalten"); private Funktion createUnionWithElements ($ elem1, $ elem2) $ s1 = $ this-> funSets-> singletonSet ($ elem1); $ s2 = $ this-> funSets-> singletonSet ($ elem2); $ this-> funSets-> union ($ s1, $ s2) zurückgeben;
Wir erstellen ein Set mit drei Elementen: 1, 2, 3. Und wir fügen es in die Variable ein $ u123
So ist es in unseren Tests leicht zu erkennen. Dann definieren wir eine Funktion, die wir auf den Test anwenden möchten, und platzieren sie in dem Test $ Bedingung
. Zum Schluss rufen wir Filter auf $ u123
mit einstellen $ Bedingung
und legen Sie das Ergebnis in $ filtersSet
. Dann führen wir Assertionen mit aus "enthält"
um festzustellen, ob das Set so aussieht, wie wir es wollen. Unsere Bedingungsfunktion ist einfach. Sie gibt true zurück, wenn das Element größer als eins ist. Unser letzter Satz sollte also nur die Werte zwei und drei enthalten, und das überprüfen wir in unseren Behauptungen.
Filter für öffentliche Funktionen ($ set, $ condition) return-Funktion ($ otherElem) use ($ set, $ condition) if ($ condition ($ otherElem)) return $ this-> enthält ($ set, $ otherElem); falsch zurückgeben; ;
Und jetzt gehts. Wir haben eine Filterung mit nur drei Zeilen Code implementiert. Wenn die Bedingung für das bereitgestellte Element zutrifft, führen wir eine Menge der Menge für das Element aus. Wenn nicht, kehren wir einfach zurück falsch
. Das ist es.
Im nächsten Schritt erstellen Sie verschiedene Schleifenfunktionen. Der erste, "für alle()", wird ein nehmen $ set
und ein $ Bedingung
und zurück wahr
ob $ Bedingung
gilt für alle Elemente der $ set
. Dies führt zu folgendem Test:
Funktion testForAllCorrectlyTellsIfAllElementsSatisfyCondition () $ u123 = $ this-> createUnionWith123 (); $ higherThanZero = Funktion ($ elem) return $ elem> 0; ; $ higherThanOne = Funktion ($ elem) return $ elem> 1; ; $ higherThanTwo = Funktion ($ elem) return $ elem> 2; ; $ this-> assertTrue ($ this-> funSets-> forall ($ u123, $ higherThanZero)); $ this-> assertFalse ($ this-> funSets-> forall ($ u123, $ higherThanOne)); $ this-> assertFalse ($ this-> funSets-> forall ($ u123, $ higherThanTwo));
Wir haben das extrahiert $ u123
Erstellung aus dem vorherigen Test in eine private Methode. Dann definieren wir drei verschiedene Bedingungen: höher als null, höher als eins und höher als zwei. Da unser Set die Zahlen eins, zwei und drei enthält, sollte nur die Bedingung "höher als null" den Wert "true" zurückgeben, der Rest sollte "false" sein. In der Tat können wir den Test mit Hilfe einer anderen rekursiven Methode durchführen, die verwendet wird, um alle Elemente zu durchlaufen.
private $ bound = 1000; private Funktion für allIterator ($ currentValue, $ set, $ condition) if ($ currentValue> $ this-> bound) Rückgabe true; elseif ($ this-> enthält ($ set, $ currentValue)) return $ condition ($ currentValue) && $ this-> forallIterator ($ currentValue + 1, $ set, $ condition); sonst Rückgabe von $ this-> forallIterator ($ currentValue + 1, $ set, $ condition); public function forall ($ set, $ condition) return $ this-> forallIterator (- $ this-> gebunden, $ set, $ condition);
Wir definieren zunächst einige Grenzen für unser Set. Die Werte müssen zwischen -1000 und +1000 liegen. Dies ist eine vernünftige Einschränkung, die wir auferlegen, um dieses Beispiel einfach genug zu halten. Die Funktion "für alle"
ruft die private Methode auf "forallIterator"
mit den erforderlichen Parametern, um rekursiv zu entscheiden, ob alle Elemente die Bedingung erfüllen. In dieser Funktion testen wir zuerst, ob wir uns außerhalb der Grenzen befinden. Wenn ja, kehre zurück. Prüfen Sie dann, ob unser Set den aktuellen Wert enthält, und geben Sie den aktuellen, auf die Bedingung angewendeten Wert zusammen mit einem logischen "UND" mit einem rekursiven Aufruf an uns selbst mit dem nächsten Wert zurück. Ansonsten rufen Sie uns einfach mit dem nächsten Wert an und geben Sie das Ergebnis zurück.
Das funktioniert gut, wir können es genauso implementieren wie "exists ()"
. Das kehrt zurück wahr
wenn eines der Elemente die Bedingung erfüllt.
private Funktion existsIterator ($ currentValue, $ set, $ condition) if ($ currentValue> $ this-> bound) return false; elseif ($ this-> enthält ($ set, $ currentValue)) liefert $ condition ($ currentValue) || $ this-> existsIterator ($ currentValue + 1, $ set, $ condition); sonst Rückgabe von $ this-> existsIterator ($ currentValue + 1, $ set, $ condition); public function exists ($ set, $ condition) return $ this-> existsIterator (- $ this-> gebunden, $ set, $ condition);
Der einzige Unterschied ist, dass wir wiederkommen falsch
Wenn außerhalb der Grenzen und wir verwenden "ODER" anstelle von "UND" im zweiten, wenn.
Jetzt, "Karte()"
wird anders, einfacher und kürzer sein.
Öffentliche Funktionskarte ($ set, $ action) return-Funktion ($ currentElem) use ($ set, $ action) return $ this-> exists ($ set, Funktion ($ elem) use ($ currentElem, $ action) return $ currentElem == $ action ($ elem);); ;
Mapping bedeutet, dass wir eine Aktion auf alle Elemente einer Menge anwenden. Für eine Karte benötigen wir keinen Helfer-Iterator, wir können ihn wiederverwenden "exists ()"
und die Elemente von "Existieren" zurückgeben, die das Ergebnis von erfüllen $ action
angewendet $ element
. Dies kann auf dem ersten Standort nicht offensichtlich sein. Sehen wir uns also an, was passiert.
1, 2
und die Aktion $ element * 2 (doppelt)
zur Karte.existiert
mit dem Set 1, 2
und die Bedingungsfunktion $ currentElement
gleich $ elem * 2
.existiert ()
wird über alle Elemente zwischen -1000 und +1000, unsere Grenzen, iterieren. Wenn es ein Element findet, ist das Doppelte dessen, was kommt "enthält"
(der Wert von $ currentElement
) es wird zurückkehren wahr
.wahr
für den Aufruf von enthält mit Wert zwei, wenn der mit zwei multiplizierte Wert des Stroms zwei ergibt. Für das erste Element des Sets, eins, wird es bei zwei true zurückgegeben. Für das zweite Element zwei auf Wert vier. Nun, funktionale Programmierung macht Spaß, ist aber bei PHP alles andere als ideal. Daher empfehle ich Ihnen nicht, ganze Anwendungen auf diese Weise zu schreiben. Nachdem Sie nun gelernt haben, was PHP funktional tun kann, können Sie Teile dieses Wissens in Ihren täglichen Projekten anwenden. Hier ist ein Beispiel für ein Authentifizierungsmodul. Das AuthPlugin
class stellt eine Methode bereit, die einen Benutzer und ein Kennwort empfängt und den Benutzer magisch authentifiziert und seine Berechtigungen setzt.
class AuthPlugin private $ permissions = array (); Funktion authenticate ($ username, $ password) $ this-> verifyUser ($ username, $ password); $ adminModules = neue AdminModules (); $ this-> permissions [] = $ adminModules-> allowRead ($ username); $ this-> permissions [] = $ adminModules-> allowWrite ($ username); $ this-> permissions [] = $ adminModules-> allowExecute ($ username); private Funktion verifyUser ($ Benutzername, $ Kennwort) //… BENUTZER- / PASS-ÜBERPRÜFUNG //… LOAD USER DETAILS, ETC.
Das hört sich vielleicht OK an, hat aber ein riesiges Problem. 80% der "authentifizieren()"
Methode verwendet Informationen aus der "AdminModule"
. Dies schafft eine sehr starke Abhängigkeit.
Es wäre viel sinnvoller, die drei Aufrufe anzunehmen und eine einzige Methode zu erstellen AdminModule
.
Also, indem wir die Generation in bewegen AdminModule
Es ist uns gelungen, drei Abhängigkeiten auf nur eine zu reduzieren. Die öffentliche Schnittstelle von AdminModule
wurde auch von drei auf nur eine Methode reduziert. Wir sind jedoch noch nicht da. AuthPlugin
noch direkt abhängig von AdminModule
.
Wenn unser Authentifizierungs-Plugin von einem beliebigen Modul verwendet werden soll, müssen wir eine gemeinsame Schnittstelle für diese Module definieren. Lassen Sie uns die Abhängigkeit einführen und eine Schnittstelle einführen.
class AuthPlugin private $ permissions = array (); privates $ appModule; Funktion __construct (ApplicationModule $ appModule) $ this-> appModule = $ appModule; Funktion authenticate ($ username, $ password) $ this-> verifyUser ($ username, $ password); $ this-> permissions = array_merge ($ this-> permissions, $ this-> appModule-> getPermissions ($ username)); private Funktion verifyUser ($ Benutzername, $ Kennwort) //… BENUTZER- / PASS-ÜBERPRÜFUNG //… LOAD USER DETAILS, ETC.
AuthPlugin
habe einen Konstruktor bekommen. Es erhält einen Parameter vom Typ Anwendungsmodul
, eine Schnittstelle und Anrufe "getPermissions ()"
auf diesem injizierten Objekt.
interface ApplicationModule öffentliche Funktion getPermissions ($ username);
Anwendungsmodul
Definiert eine einzelne öffentliche Methode, "getPermissions ()"
, mit einem Benutzernamen als Parameter.
Klasse AdminModules implementiert ApplicationModule // […]
Endlich, AdminModule
muss das implementieren Anwendungsmodul
Schnittstelle.
Das ist jetzt viel besser. Unsere AuthPlugin
hängt nur von einer Schnittstelle ab. AdminModule
hängt von der gleichen Schnittstelle ab, also die AuthPlugin
wurde Modulagnostiker. Wir können eine beliebige Anzahl von Modulen erstellen, die alle implementieren Anwendungsmodul
, und AuthPlugin
kann mit allen zusammenarbeiten.
Eine andere Möglichkeit, die Abhängigkeit umzukehren und zu machen AdminModule
, oder ein beliebiges anderes Modul, um die AuthPlugin
ist, in diese Module eine Abhängigkeit zu injizieren AuthPlugin
. AuthPlugin
bietet eine Möglichkeit zum Festlegen der Authentifizierungsfunktion, und jede Anwendung sendet eine eigene "Erlaubnis bekommen()"
Funktion.
class AdminModules privates $ authPlugin; Funktion __construct (Authentitcation $ authPlugin) $ this-> authPlugin = $ authPlugin; private Funktion allowRead ($ username) return "yes"; private Funktion allowWrite ($ username) return "no"; private Funktion allowExecute ($ username) return $ username == "joe"? "ja Nein"; private Funktion authenticate () $ this-> authPlugin-> setPermissions (Funktion ($ username) $ permissions = array (); $ permissions [] = $ this-> allowRead ($ username); $ permissions [] = $) this-> allowWrite ($ username); $ permissions [] = $ this-> allowExecute ($ username); $ permissions zurückgeben;); $ this-> authPlugin-> authenticate ();
Wir fangen mit an AdminModule
. Es implementiert nichts mehr. Es verwendet jedoch ein injiziertes Objekt, das die Authentifizierung implementieren muss. Im AdminModule
es wird eine geben "authentifizieren()"
Methode, die aufgerufen wird "Berechtigungen festlegen()"
auf AuthPlugin
und übergeben Sie die Funktion, die verwendet werden muss.
interface Authentication function setPermissions ($ permissionGrantingFunction); Funktion authenticate ();
Die Authentifizierungsschnittstelle definiert einfach die beiden Methoden.
class AuthPlugin implementiert Authentication private $ permissions = array (); privates $ appModule; private $ permissionsFunction; Funktion __construct (ApplicationModule $ appModule) $ this-> appModule = $ appModule; Funktion authenticate ($ username, $ password) $ this-> verifyUser ($ username, $ password); $ this-> permissions = $ this-> permissionsFunction ($ username); private Funktion verifyUser ($ Benutzername, $ Kennwort) //… BENUTZER- / PASS-ÜBERPRÜFUNG //… LOAD USER DETAILS, ETC. public function setPermissions ($ permissionGrantingFunction) $ this-> permissionsFunction = $ permissionGrantingFunction;
Endlich, AuthPlugin
implementiert die Authentifizierung und legt die eingehende Funktion in einem privaten Klassenattribut fest. Dann, "Authentifizierung()"
wird eine dumme Methode. Es ruft einfach die Funktion auf und setzt dann den Rückgabewert. Es ist völlig entkoppelt von dem, was kommt.
Wenn wir das Schema betrachten, gibt es zwei wichtige Änderungen:
AdminModule
, AuthPlugin
implementiert die Schnittstelle.AuthPlugin
wird "Rückruf" AdminModule
, oder was auch immer ein anderes Modul in der Berechtigungsfunktion gesendet hat.Auf diese Frage gibt es keine richtige Antwort. Ich würde behaupten, dass der objektorientierte Ansatz angemessener ist, wenn der Prozess zur Bestimmung der Berechtigungen ziemlich vom Anwendungsmodul abhängt. Wenn Sie jedoch der Meinung sind, dass jedes Anwendungsmodul in der Lage sein sollte, eine Authentifizierungsfunktion bereitzustellen, und Ihre AuthPlugin
ist nur ein Grundgerüst, das die Authentifizierungsfunktionalität bereitstellt, aber ohne etwas über Benutzer und Verfahren zu wissen, können Sie sich für den funktionalen Ansatz entscheiden.
Der funktionale Ansatz macht Ihre AuthPlugin
sehr abstrakt und darauf kann man sich verlassen. Wenn Sie jedoch planen, Ihre AuthPlugin
Wenn Sie mehr tun und mehr über Benutzer und Ihr System wissen möchten, wird es zu konkret, und Sie möchten sich nicht darauf verlassen. Wählen Sie in diesem Fall den objektorientierten Weg und lassen Sie den Beton AuthPlugin
hängen von den abstrakteren Anwendungsmodulen ab.