De nieuwe hype in programmeren draait allemaal om functionele programmeerparadigma's. Functionele talen worden steeds vaker gebruikt in steeds betere en betere applicaties. Scala, Haskel, etc. zijn bloeiende en andere, meer conservatieve talen zoals Java begonnen enkele functionele programmeerparadigma's aan te nemen (zie sluitingen in Java7 en lui eval voor lijsten in Java8). Wat echter maar weinig mensen weten, is dat PHP vrij veelzijdig is als het gaat om functioneel programmeren. Alle belangrijke functionele programmeerconcepten kunnen worden uitgedrukt in PHP. Dus, als je nog niet vertrouwd bent met functioneel programmeren, wees dan voorbereid op het feit dat je je hoofd breekt, en als je al bekend bent met functioneel programmeren, wees dan bereid om veel plezier te hebben met deze tutorial.
Zonder programmeerparadigma's zouden we kunnen doen wat we willen, op elke manier die we willen. Hoewel dit zou leiden tot extreme flexibiliteit, zou dit ook leiden tot onmogelijke architecturen en zeer opgeblazen code. Programmeparadigma's zijn dus uitgevonden om ons, programmeurs, te helpen op een specifieke manier over een specifiek programma na te denken en op deze manier ons vermogen om onze oplossing te uiten te beperken..
Elk programmeerparadigma neemt een vrijheid van ons weg:
In functioneel programmeren heeft u geen gegevens vertegenwoordigd door variabelen.
In functioneel programmeren alles is een functie. En ik bedoel alles. Een set, zoals in de wiskunde, kan bijvoorbeeld worden weergegeven als verschillende functies. Een array of lijst is ook een functie of een groep functies.
Bij objectgeoriënteerd programmeren is alles een object. En een object is een verzameling gegevens en methoden die acties uitvoeren op die gegevens. Objecten hebben een toestand, een vluchtige, veranderlijke toestand.
In functioneel programmeren heeft u geen gegevens vertegenwoordigd door variabelen. Er zijn geen gegevenscontainers. Gegevens worden niet aan een variabele toegewezen. Sommige waarden kunnen worden gedefinieerd en toegewezen. In de meeste gevallen zijn het echter functies die zijn toegewezen aan "variabelen". Ik heb "variabelen" tussen aanhalingstekens geplaatst, omdat ze in functioneel programmeren staan onveranderlijk. Hoewel de meeste functionele programmeertalen de onveranderlijkheid niet afdwingen, dwingen de meeste objectgeoriënteerde talen geen objecten af, als je de waarde na een opdracht verandert, doe je niet meer puur functioneel programmeren meer.
Omdat u geen waarden hebt toegewezen aan variabelen, heeft u functionele programmering geen staat.
Vanwege geen staat en geen toewijzingen, in functioneel programmeren functies hebben geen bijwerking. En vanwege de drie voorgaande redenen, functies zijn altijd voorspelbaar. Dit betekent in feite dat als je een functie met dezelfde parameters steeds opnieuw en opnieuw oproept ... je altijd hetzelfde resultaat zult hebben. Dit is een enorm voordeel ten opzichte van objectgeoriënteerd programmeren en vermindert de complexiteit van multi-threaded en massively multi-threaded applicaties aanzienlijk.
Maar als we alles in functies willen uitdrukken, moeten we ze aan parameters kunnen toewijzen of ze uit andere functies kunnen retourneren. Dus functionele programmering vereist de ondersteuning voor hogere orde functies. Dit betekent in feite dat een functie kan worden toegewezen aan een "variabele", wordt verzonden als een parameter naar een andere functie en wordt geretourneerd als een resultaat van een functie.
Ten slotte, omdat we geen waarden in variabelen hebben, terwijl en voor loops ongebruikelijk zijn voor functionele programmering en worden vervangen door recursie.
Genoeg gepraat en filosofie voor één les. Laten we coderen!
Stel een PHP-project op in uw favoriete IDE- of code-editor. Maak er een in "Tests"
map. Maak twee bestanden: FunSets.php
in de map van het project, en FunSetsTest.php
in de map Tests. We zullen een applicatie maken met tests die het concept sets zal vertegenwoordigen.
In de wiskunde is een verzameling een verzameling verschillende objecten, die op zich als een object worden beschouwd. (Wikipedia)
Dat betekent in feite dat sets een heleboel dingen op één plek zijn. Deze sets kunnen en worden gekenmerkt door wiskundige bewerkingen: bonden, snijpunten, verschillen, enz. En door bruikbare eigenschappen zoals: bevat.
Dus laten we code! Maar wacht. Hoe? Om de concepten van functioneel programmeren te respecteren, moeten we de volgende beperkingen op onze code toepassen:
Er zijn geen beperkingen van toepassing op tests. Vanwege de aard van PHPUnit zullen we daar klassieke object georiënteerde PHP-code gebruiken. Om onze tests beter te kunnen verwerken, zullen we onze productiecode in één enkele klasse verwerken.
Als je een ervaren programmeur bent, maar niet bekend bent met functioneel programmeren, nu is het tijd om te stoppen met denken zoals gewoonlijk en wees klaar om je comfortzone te verlaten. Vergeet al uw eerdere manieren van redeneren over een probleem en stel u alle functies voor.
De bepalende functie van een set is de "bevat" -methode.
functie bevat ($ set, $ elem) return $ set ($ elem);
OK ... Dit is niet zo voor de hand liggend, dus laten we eens kijken hoe we het zouden gebruiken.
$ set = function ($ element) return true;; bevat ($ set, 100);
Nou, dit verklaart het een beetje beter. De functie "Bevat"
heeft twee parameters:
$ set
- vertegenwoordigt een set gedefinieerd als een functie.$ elem
- vertegenwoordigt een element gedefinieerd als een waarde.In deze context, dat alles "Bevat"
hoeft te doen is om de functie in te passen $ set
met de parameter $ elem
. Laten we alles in een test verwerken.
class FunSetsTest breidt PHPUnit_Framework_TestCase uit private $ funSets; beschermde functie setUp () $ this-> funSets = nieuwe FunSets (); function testContainsIsImplemented () // We karakteriseren een set door zijn functie bevat. Het is de basisfunctie van een set. $ set = function ($ element) return true;; $ this-> assertTrue ($ this-> funSets-> bevat ($ set, 100));
En verpak onze productiecode erin FunSets.php
in een klas:
class FunSets public function contains ($ set, $ elem) return $ set ($ elem);
U kunt deze test daadwerkelijk uitvoeren en deze zal slagen. De set die we hebben gedefinieerd voor deze test is slechts een functie die altijd true retourneert. Het is een "echte set".
Als het vorige hoofdstuk een beetje verwarrend of nutteloos leek in de logica, zal dit het een beetje verduidelijken. We willen een set definiëren met een enkel element, een singleton-set. Denk eraan, dit moet een functie zijn, en we zullen het willen gebruiken zoals in de onderstaande test.
function testSingletonSetContainsSingleElement () // Een singleton-set wordt gekenmerkt door een functie die doorgegeven aan bevat, retourneert waar voor het enkele element // doorgegeven als parameter. Met andere woorden, een singleton is een set met een enkel element. $ singleton = $ this-> funSets-> singletonSet (1); $ this-> assertTrue ($ this-> funSets-> bevat ($ singleton, 1));
We moeten een functie definiëren genaamd "SingeltonSet"
met een parameter die een element van de set voorstelt. In de test is dat de nummer één (1). Dan verwachten we onze bevat
methode, wanneer deze wordt aangeroepen met een singleton-functie, om terug te keren waar
als de verzonden parameter gelijk is aan één. De code die de test doet slagen is als volgt:
openbare functie singletonSet ($ elem) return function ($ otherElem) use ($ elem) return $ elem == $ otherElem; ;
Wauw! Dat is gek. Dus de functie "SingletonSet"
krijgt als parameter een element als $ elem
. Vervolgens wordt een andere functie geretourneerd die een parameter heeft $ otherElem
en deze tweede functie zal vergelijken $ elem
naar $ otherElem
.
Dus, hoe werkt dit? Ten eerste, deze regel:
$ singleton = $ this-> funSets-> singletonSet (1);
wordt omgezet in wat "SingletonSet (1)"
komt terug:
$ singleton = function ($ otherElem) return 1 == $ otherElem; ;
Dan "bevat ($ singleton, 1)"
wordt genoemd. Die op zijn beurt roept wat er in zit $ Singleton
. Dus de code wordt:
$ Singleton (1)
Die de code erin uitvoert $ otherElem
de waarde hebben.
return 1 == 1
Dat is natuurlijk waar en onze test gaat voorbij.
Ben je al aan het glimlachen? Heb je het gevoel dat je hersenen beginnen te koken? Dat deed ik zeker toen ik dit voorbeeld voor het eerst schreef in Scala en ik deed het opnieuw toen ik dit voorbeeld voor het eerst schreef in PHP. Ik vind dit buitengewoon. We zijn erin geslaagd om een set te definiëren, met één element, met de mogelijkheid om te controleren of deze de waarde bevat die we hebben doorgegeven. We hebben dit allemaal gedaan zonder een enkele toewijzing van waarde. We hebben geen variabele die de waarde één bevat of een status heeft. Geen staat, geen toewijzing, geen veranderlijkheid, geen loops. We zijn hier op de goede weg.
Nu we een set met één waarde kunnen maken, moeten we een set met verschillende waarden kunnen maken. De voor de hand liggende manier om dit te doen, is om de vakbondsoperatie op onze sets te definiëren. De vereniging van twee singleton-sets vertegenwoordigt een andere unie met beide waarden. Ik wil dat u even nadenkt over de oplossing voordat u naar de code scrolt, misschien een piek neemt op de onderstaande tests.
function testUnionContainsAllElements () // Een vak wordt gekenmerkt door een functie die 2 sets krijgt als parameters en alle beschikbare sets bevat // We kunnen alleen singletons maken op dit punt, dus we creëren 2 singletons en verenigen ze $ s1 = $ this -> funSets-> singletonSet (1); $ s2 = $ this-> funSets-> singletonSet (2); $ union = $ this-> funSets-> union ($ s1, $ s2); // Controleer nu of zowel 1 als 2 deel uitmaken van de unie $ this-> assertTrue ($ this-> funSets-> contains ($ union, 1)); $ this-> assertTrue ($ this-> funSets-> bevat ($ union, 2)); // ... en dat het geen 3 $ this bevat -> assertFalse ($ this-> funSets-> contains ($ union, 3));
We willen een functie genaamd "unie"
dat krijgt twee parameters, beide sets. Onthoud, sets zijn slechts functies voor ons, dus onze "unie"
functie krijgt twee functies als parameters. Dan willen we kunnen checken "Bevat"
als de unie een element bevat of niet. Zo onze "unie"
functie moet een andere functie teruggeven die "Bevat"
kan gebruiken.
openbare functie union ($ s1, $ s2) return function ($ otherElem) use ($ s1, $ s2) return $ this-> contains ($ s1, $ otherElem) || $ this-> bevat ($ s2, $ otherElem); ;
Dit werkt eigenlijk best goed. En het is perfect geldig, zelfs als je unie wordt opgeroepen met een andere unie plus een singleton. Het roept bevat
in zichzelf voor elke parameter. Als het een unie is, wordt deze opnieuw gebruikt. Het is zo simpel.
We kunnen dezelfde voeringlogica toepassen met kleine wijzigingen om de volgende twee belangrijkste functies te krijgen die een set kenmerken: intersectie - bevat alleen de gemeenschappelijke elementen tussen twee sets - en verschil - bevat alleen die elementen uit de eerste set die geen deel uitmaken van de tweede set.
openbare functie kruisen ($ s1, $ s2) return-functie ($ otherElem) gebruik ($ s1, $ s2) return $ this-> bevat ($ s1, $ otherElem) && $ this-> bevat ($ s2, $ otherElem); ; public function diff ($ s1, $ s2) return function ($ otherElem) use ($ s1, $ s2) return $ this-> contains ($ s1, $ otherElem) &&! $ this-> contains ($ s2 , $ otherElem); ;
Ik zal je niet overspoelen met de testcode voor deze twee methoden. De tests zijn geschreven en u kunt ze controleren als u in de bijgevoegde code kijkt.
Welnu, dit is een beetje ingewikkelder, we zullen dit niet kunnen oplossen met een enkele regel code. Een filter is een functie die twee parameters gebruikt: een set- en een filterfunctie. Het past de filterfunctie op een set toe en retourneert een andere set die alleen de elementen bevat die voldoen aan de filterfunctie. Om het beter te begrijpen, hier is de test ervoor.
function testFilterContainsOnlyElementsThatMatchConditionFunction () $ u12 = $ this-> createUnionWithElements (1, 2); $ u123 = $ this-> funSets-> union ($ u12, $ this-> funSets-> singletonSet (3)); // Filterregel, zoek elementen groter dan 1 (betekent 2 en 3) $ condition = function ($ elem) return $ elem> 1;; // Gefilterde set $ filteredSet = $ this-> funSets-> filter ($ u123, $ voorwaarde); // Controleer gefilterde set bevat geen 1 $ this-> assertFalse ($ this-> funSets-> bevat ($ filteredSet, 1), "Should niet 1"); // Check it bevat 2 en 3 $ this-> assertTrue ($ this-> funSets-> bevat ($ filteredSet, 2), "Should contains 2"); $ this-> assertTrue ($ this-> funSets-> bevat ($ filteredSet, 3), "Should contains 3"); private function createUnionWithElements ($ elem1, $ elem2) $ s1 = $ this-> funSets-> singletonSet ($ elem1); $ s2 = $ this-> funSets-> singletonSet ($ elem2); return $ this-> funSets-> union ($ s1, $ s2);
We maken een set met drie elementen: 1, 2, 3. En we plaatsen het in de variabele $ U123
dus het is gemakkelijk herkenbaar in onze tests. Vervolgens definiëren we een functie die we willen toepassen op de test en plaatsen we deze $ conditie
. Ten slotte noemen we filter ons $ U123
Ingesteld met $ conditie
en plaats de resulterende set erin $ filteredSet
. Dan voeren we beweringen uit met "Bevat"
om te bepalen of de set eruitziet hoe we willen. Onze conditiefunctie is eenvoudig, het geeft waar terug als het element groter is dan één. Dus onze definitieve set zou alleen de waarden twee en drie moeten bevatten, en dit is wat we in onze beweringen controleren.
filter voor openbare functie ($ set, $ voorwaarde) return function ($ otherElem) use ($ set, $ condition) if ($ condition ($ otherElem)) return $ this-> contains ($ set, $ otherElem); return false; ;
En alsjeblieft. We hebben het filteren geïmplementeerd met slechts drie regels code. Om precies te zijn: als de voorwaarde van toepassing is op het geleverde element, voeren we een aantal op de reeks voor dat element uit. Zo niet, komen we gewoon terug vals
. Dat is het.
De volgende stap is het maken van verschillende looping-functies. De eerste, "voor iedereen()", zal een $ set
en een $ conditie
En terugkomen waar
als $ conditie
is van toepassing op alle elementen van de $ set
. Dit leidt tot de volgende test:
function testForAllCorrectlyTellsIfAllElementsSatisfyCondition () $ u123 = $ this-> createUnionWith123 (); $ higherThanZero = function ($ elem) return $ elem> 0; ; $ higherThanOne = function ($ elem) return $ elem> 1; ; $ higherThanTwo = function ($ 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));
We hebben het $ U123
creatie van de vorige test naar een private methode. Vervolgens definiëren we drie verschillende omstandigheden: hoger dan nul, hoger dan één en hoger dan twee. Omdat onze set de getallen één, twee en drie bevat, moet alleen de hogere dan nul-voorwaarde waar retourneren, de rest moet onwaar zijn. Inderdaad, we kunnen de test laten slagen met behulp van een ander recursief methode gebruik om alle elementen te itereren.
privé $ gebonden = 1000; private functie voorallIterator ($ currentValue, $ set, $ condition) if ($ currentValue> $ this-> bound) return true; elseif ($ this-> contains ($ set, $ currentValue)) return $ condition ($ currentValue) && $ this-> forallIterator ($ currentValue + 1, $ set, $ condition); else return $ this-> forallIterator ($ currentValue + 1, $ set, $ condition); openbare functie forall ($ set, $ condition) retourneer $ this-> voorallIterator (- $ this-> bound, $ set, $ condition);
We beginnen met het definiëren van enkele limieten voor onze set. De waarden moeten liggen tussen -1000 en +1000. Dit is een redelijke beperking die we opleggen om dit voorbeeld eenvoudig genoeg te houden. De functie "voor iedereen"
zal de private methode aanroepen "ForallIterator"
met de nodige parameters om recursief te beslissen of alle elementen de voorwaarde respecteren. In deze functie testen we eerst of we geen grenzen hebben. Zo ja, retourneer waar. Controleer vervolgens of onze set de huidige waarde bevat en retourneer de huidige waarde toegepast op de voorwaarde samen met een logische "AND" met een recursieve oproep naar onszelf met de volgende waarde. Anders, bel ons gewoon met de volgende waarde en retourneer het resultaat.
Dit werkt prima, we kunnen het op dezelfde manier implementeren als "Bestaat ()"
. Dit komt terug waar
als een van de elementen voldoet aan de voorwaarde.
private function existsIterator ($ currentValue, $ set, $ condition) if ($ currentValue> $ this-> bound) return false; elseif ($ this-> contains ($ set, $ currentValue)) return $ condition ($ currentValue) || $ this-> existsIterator ($ currentValue + 1, $ set, $ condition); anders return $ this-> existsIterator ($ currentValue + 1, $ set, $ condition); public function exists ($ set, $ condition) return $ this-> existsIterator (- $ this-> bound, $ set, $ condition);
Het enige verschil is dat we terugkeren vals
wanneer buiten de grenzen en gebruiken we "OF" in plaats van "AND" in de tweede als.
Nu, "kaart()"
zal anders, eenvoudiger en korter zijn.
openbare functiekaart ($ set, $ actie) return function ($ currentElem) gebruik ($ set, $ action) return $ this-> exists ($ set, function ($ elem) use ($ currentElem, $ action) return $ currentElem == $ actie ($ elem);); ;
Mapping betekent dat we een actie toepassen op alle elementen van een set. Voor de kaart hebben we geen helper-iterator nodig, we kunnen deze hergebruiken "Bestaat ()"
en retourneer die elementen van "bestaat" die voldoen aan het resultaat van $ actie
toegepast op $ element
. Dit is misschien niet meteen duidelijk op de eerste site, dus laten we eens kijken wat er gebeurt.
1, 2
en de actie $ element * 2 (dubbel)
in kaart brengen.bestaat
met de set 1, 2
en de conditiefunctie $ currentElement
is gelijk aan $ elem * 2
.bestaat ()
zal itereren over alle elementen tussen -1000 en +1000, onze grenzen. Wanneer het een element vindt, het dubbele van wat er vandaan komt "Bevat"
(de waarde van $ currentElement
) het zal terugkeren waar
.waar
voor de aanroep naar bevat met waarde twee, wanneer de waarde van de stroom vermenigvuldigd met twee, resulteert in twee. Dus voor het eerste element van de set, één, zal het op twee terugkeren. Voor het tweede element, twee, op waarde vier. Welnu, functioneel programmeren is leuk, maar het is verre van ideaal in PHP. Dus ik raad je niet aan om hele applicaties op deze manier te schrijven. Nu u echter hebt geleerd wat PHP functioneel kan doen, kunt u delen van deze kennis toepassen in uw dagelijkse projecten. Hier is een voorbeeld van een authenticatiemodule. De AuthPlugin
class biedt een methode die een gebruiker en wachtwoord ontvangt en doet zijn magie om de gebruiker te authenticeren en zijn rechten in te stellen.
class AuthPlugin private $ permissions = array (); function authenticate ($ gebruikersnaam, $ wachtwoord) $ this-> verifyUser ($ gebruikersnaam, $ wachtwoord); $ adminModules = nieuwe AdminModules (); $ this-> permissies [] = $ adminModules-> allowRead ($ gebruikersnaam); $ this-> permissies [] = $ adminModules-> allowWrite ($ gebruikersnaam); $ this-> permissies [] = $ adminModules-> allowExecute ($ gebruikersnaam); persoonlijke functie verifyUser ($ gebruikersnaam, $ wachtwoord) // ... DOE DE GEBRUIKER / PAS CONTROLEREN // ... LAAD GEBRUIKERSGEGEVENS, ENZ.
Dit klinkt misschien goed, maar het heeft een enorm probleem. 80% van de "Authenticeren ()"
methode gebruikt informatie uit de "AdminModules"
. Dit creëert een zeer sterke afhankelijkheid.
Het zou veel redelijker zijn om de drie oproepen aan te nemen en een enkele methode aan te maken AdminModules
.
Dus door de generatie te verplaatsen naar AdminModules
we zijn erin geslaagd om drie afhankelijkheden tot slechts één te reduceren. De openbare interface van AdminModules
werd ook teruggebracht van drie naar slechts één enkele methode. We zijn er echter nog niet. AuthPlugin
nog steeds direct afhankelijk van AdminModules
.
Als onze authenticatie-plug-in bruikbaar is voor elke module, moeten we een gemeenschappelijke interface voor deze modules definiëren. Laten we de afhankelijkheid injecteren en een interface introduceren.
class AuthPlugin private $ permissions = array (); privé $ appModule; function __construct (ApplicationModule $ appModule) $ this-> appModule = $ appModule; function authenticate ($ gebruikersnaam, $ wachtwoord) $ this-> verifyUser ($ gebruikersnaam, $ wachtwoord); $ this-> permissions = array_merge ($ this-> permissions, $ this-> appModule-> getPermissions ($ username)); persoonlijke functie verifyUser ($ gebruikersnaam, $ wachtwoord) // ... DOE DE GEBRUIKER / PAS CONTROLEREN // ... LAAD GEBRUIKERSGEGEVENS, ENZ.
AuthPlugin
heb een constructeur. Het krijgt een parameter van het type ApplicationModule
, een interface en oproepen "GetPermissions ()"
op dit geïnjecteerde object.
interface ApplicationModule openbare functie getPermissions ($ gebruikersnaam);
ApplicationModule
definieert een enkele openbare methode, "GetPermissions ()"
, met een gebruikersnaam als een parameter.
klasse AdminModules implementeert ApplicationModule // [...]
Tenslotte, AdminModules
moet het ApplicationModule
interface.
Dit is nu veel beter. Onze AuthPlugin
hangt alleen af van een interface. AdminModules
is afhankelijk van dezelfde interface, dus de AuthPlugin
werd module agnostisch. We kunnen een willekeurig aantal modules creëren, allemaal implementerend ApplicationModule
, en AuthPlugin
zal met al deze kunnen werken.
Een andere manier om de afhankelijkheid om te keren en te maken AdminModule
, of een andere module, om de AuthPlugin
is om in deze modules een afhankelijkheid te injecteren AuthPlugin
. AuthPlugin
biedt een manier om de authenticatiefunctie in te stellen en elke toepassing verzendt deze naar zijn eigen "toestemming krijgen()"
functie.
class AdminModules private $ authPlugin; function __construct (Authentitcation $ authPlugin) $ this-> authPlugin = $ authPlugin; private functie allowRead ($ gebruikersnaam) return "yes"; private functie allowWrite ($ username) return "no"; private functie allowExecute ($ gebruikersnaam) return $ username == "joe"? "Ja nee"; private function authenticate () $ this-> authPlugin-> setPermissions (function ($ username) $ permissions = array (); $ permissions [] = $ this-> allowRead ($ username); $ permissions [] = $ this-> allowWrite ($ gebruikersnaam); $ permissies [] = $ this-> allowExecute ($ gebruikersnaam); retourneer $ permissies;); $ This-> authPlugin-> authenticeren ();
We beginnen met AdminModule
. Het implementeert niets meer. Het gebruikt echter een geïnjecteerd object dat authenticatie moet implementeren. In AdminModule
er zal een "Authenticeren ()"
methode die zal bellen "SetPermissions ()"
op AuthPlugin
en geef de functie door die moet worden gebruikt.
interface Authenticatie function setPermissions ($ permissionGrantingFunction); function authenticate ();
De authenticatie-interface definieert eenvoudig de twee methoden.
klasse AuthPlugin implementeert Authenticatie private $ permissions = array (); privé $ appModule; private $ permissionsFunction; function __construct (ApplicationModule $ appModule) $ this-> appModule = $ appModule; function authenticate ($ gebruikersnaam, $ wachtwoord) $ this-> verifyUser ($ gebruikersnaam, $ wachtwoord); $ this-> permissies = $ this-> permissionsFunction ($ gebruikersnaam); persoonlijke functie verifyUser ($ gebruikersnaam, $ wachtwoord) // ... DOE DE GEBRUIKER / PAS CONTROLEREN // ... LAAD GEBRUIKERSGEGEVENS, ENZ. public function setPermissions ($ permissionGrantingFunction) $ this-> permissionsFunction = $ permission GrantingFunction;
Tenslotte, AuthPlugin
implementeert Verificatie en stelt de inkomende functie in een kenmerk van een particuliere klasse in. Dan, "Authenticatie ()"
wordt een stomme methode. Het roept gewoon de functie op en stelt vervolgens de geretourneerde waarde in. Het is volledig ontkoppeld van alles wat binnenkomt.
Als we naar het schema kijken, zijn er twee belangrijke wijzigingen:
AdminModule
, AuthPlugin
is degene die de interface implementeert.AuthPlugin
zal "terugbellen" AdminModule
, of welke andere module dan ook verzonden in de permissiefunctie.Er is geen goed antwoord op deze vraag. Ik zou willen beweren dat als het proces van het bepalen van de machtigingen behoorlijk afhankelijk is van de applicatiemodule, dan is de objectgerichte benadering meer geschikt. Als u echter van mening bent dat elke applicatiemodule een authenticatiefunctie moet kunnen bieden, en uw AuthPlugin
is slechts een skelet dat de authenticatiefunctionaliteit biedt maar zonder iets te weten over gebruikers en procedures, dan kunt u kiezen voor de functionele aanpak.
De functionele aanpak maakt jouw AuthPlugin
erg abstract en je kunt erop vertrouwen. Als u echter van plan bent om uw AuthPlugin
om meer te doen en meer te weten over gebruikers en uw systeem, zal het te concreet worden en u wilt er niet van afhankelijk zijn. Kies in dat geval de objectgeoriënteerde manier en laat het beton AuthPlugin
afhankelijk van de meer abstracte applicatiemodules.