Whens og Whys for PHP Design Patterns

Det er mange artikler som forklarer hva designmønstre er, og hvordan de skal implementeres. Internett trenger ikke enda en av disse artiklene! I stedet, i denne artikkelen, vil vi diskutere når og Hvorfor, snarere enn hvilken og hvordan.

Jeg presenterer forskjellige situasjoner og bruksområder for mønstre, og vil også gi korte definisjoner for å hjelpe de som ikke er så kjent med disse spesifikke mønstrene. La oss komme i gang.

Publisert opplæring

Noen få uker besøker vi noen av leserens favorittinnlegg fra hele historien til nettstedet. Denne opplæringen ble først publisert i oktober 2012.

Denne artikkelen dekker noen av de forskjellige Agile Design Patterns, dokumentert i Robert C. Martins bøker. Disse mønstrene er moderne tilpasninger av de opprinnelige designmønstrene som er definert og dokumentert av The Gang of Four i 1994. Martins mønstre presenterer en mye nyere nytte av GoFs mønstre, og de fungerer bedre med moderne programmeringsteknikker og -problemer. Faktisk ble ca. 15% av de opprinnelige mønstrene erstattet med nyere mønstre, og de gjenværende mønstrene ble litt modernisert.


La oss begynne med å opprette noen objekter

Bruk et fabrikkmønster

Fabrikkmønsteret ble oppfunnet for å hjelpe programmerere til å organisere informasjonen relatert til opprettelse av objekt. Objekter har noen ganger mange konstruktørparametre; andre ganger må de fylles med standard informasjon umiddelbart etter opprettelsen. Disse objektene skal opprettes i fabrikker, og beholder all informasjon om deres opprettelse og initialisering inneholdt i et enkelt sted.

Når Bruk et fabrikkmønster når du finner deg selv å skrive kode for å samle informasjon som er nødvendig for å lage objekter.

Hvorfor: Fabrikkene bidrar til å inneholde logikken til objektopprettelse på et enkelt sted. De kan også bryte avhengigheter for å lette løs kobling og avhengighetsinjeksjon for å muliggjøre bedre testing.


Finne de dataene vi trenger

Det er to ofte brukte mønstre for å hente informasjon fra et vedvarende lag eller ekstern datakilde.

Gateway-mønsteret

Dette mønsteret definerer en kommunikasjonskanal mellom en persistensløsning og forretningslogikken. For enklere applikasjoner kan det hente eller gjenskape hele gjenstander av seg selv, men objektdannelse er fabrikkansvar i de fleste komplekse applikasjoner. Gateways henter enkelt og vedvarer rå data.

Når: Når du trenger å hente eller vedvare informasjon.

Hvorfor: Det gir et enkelt offentlig grensesnitt for kompliserte utholdenhetsoperasjoner. Den inkapsler også utholdenhetskunnskap og avkaller forretningslogikk fra persistenslogikk.

Faktisk er gateway-mønsteret bare en bestemt implementering av et annet designmønster som vi snart skal diskutere: adaptermønsteret.

Gå med proxyen

Det er tider når du ikke kan (eller ikke vil) eksponere kunnskapen om persistenslaget til bedriftsklassene dine. Proxy-mønsteret er en god måte å lure forretningsklassene dine til å tro at de allerede bruker eksisterende objekter.

Når: Du må hente informasjon fra et vedvarende lag eller ekstern kilde, men vil ikke at forretningslogikken din skal vite dette.

Hvorfor: Å tilby en ikke-påtrengende tilnærming til å skape objekter bak kulissene. Det åpner også muligheten til å hente disse objektene på fly, lat og fra forskjellige kilder.

En proxy implementerer effektivt det samme grensesnittet som et ekte objekt og etterligner funksjonaliteten. Forretningslogikken bruker bare det som om det var et reelt objekt, men faktisk produserer proxy objektet hvis man ikke eksisterer.

Det aktive objektmønsteret spilte også en rolle i tidlig multi-tasking-systemer.

Okay, okay. Det store og alt, men hvordan kan vi finne de objektene vi trenger å lage?

Spør et arkiv

Lagringsmønsteret er veldig nyttig for implementering av søkemetoder og spørrespråk. Det tar disse spørsmålene og bruker en gateway for å skaffe dataene til en fabrikk for å produsere gjenstandene du trenger.

Oppbevaringsmønsteret er forskjellig fra de andre mønstrene; Den eksisterer som en del av Domain Driven Design (DDD), og er ikke inkludert som en del av Robert C. Martins bok.

Når: Du må opprette flere objekter basert på søkekriterier, eller når du må lagre flere objekter til persistenslaget.

Hvorfor: Å la klienter som trenger spesifikke objekter å arbeide med et felles og godt isolert spørrings- og utholdsspråk. Den fjerner enda mer skapelsesrelatert kode fra forretningslogikken.

Men hva om depotet ikke finner gjenstandene? Ett alternativ ville være å returnere a NULL verdi, men det har to bivirkninger:

  • Det kaster a nektet erverv hvis du prøver å kalle en metode på en slik gjenstand.
  • Det tvinger deg til å inkludere mange null sjekker (hvis (is_null ($ param)) returnerer;) i koden din.

En bedre tilnærming er å returnere a null gjenstand.

Null Object Pattern

Et null objekt implementerer det samme grensesnittet til de andre objektene dine, men objektets medlemmer returnerer en nøytral verdi. For eksempel vil en metode som returnerer en streng, returnere en tom streng; Et annet medlem som returnerer en numerisk verdi, ville returnere null. Dette tvinger deg til å implementere metoder som ikke returnerer meningsfylte data, men du kan bruke disse objektene uten å bekymre deg om nektet å erobre eller kaste koden din med null sjekker.

Når: Du sjekker ofte for null eller du har nektet arvinger.

Hvorfor: Det kan legge til klarhet i koden din og tvinger deg til å tenke mer om oppførselen til objektene dine.

Det er ikke uvanlig å ringe mange metoder på et objekt før det kan gjøre jobben sin. Det er situasjoner når du må forberede et objekt etter opprettelsen før du virkelig kan bruke den. Dette fører til kod duplisering når du lager disse objektene på forskjellige steder.

Du trenger kommandomodelet

Når: Når du må utføre mange operasjoner for å forberede gjenstander til bruk.

Hvorfor: For å flytte kompleksitet fra forbrukskode til opprettingskode.

Dette høres bra ut, ikke sant? Faktisk er det ganske nyttig i mange situasjoner. Kommandoen mønsteret er mye brukt for å implementere transaksjoner. Hvis du legger til en enkel angre () metoden til et kommandobjekt, kan det spore alle de utøve transaksjonene det utførte og reversere dem om nødvendig.

Så nå har du ti (eller flere) kommandobjekter, og du vil at de skal kjøre samtidig. Du kan samle dem inn i et aktivt objekt.

Det aktive objektet

Det enkle og interessante aktive objektet har bare ett ansvar: Hold en liste over kommandobjekter og kjør dem.

Når: Flere lignende gjenstander må utføres med en enkelt kommando.

Hvorfor: Det tvinger klienter til å utføre en enkelt oppgave og påvirke flere objekter.

Et aktivt objekt fjerner hver kommando fra listen etter at kommandoen er utført. Det betyr at du bare kan utføre kommandoen en gang. Noen virkelige eksempler på et aktivt objekt er:

Designmønstre er her for å løse problemer.

  • Handlevogn - Utføre en kjøpe() kommandoen på hvert produkt fjerner dem fra handlekurven.
  • Finansielle transaksjoner - Gruppering av transaksjoner i en enkelt liste og utførelse av dem med et enkelt anrop til listelederens aktive objekt ville fjerne transaksjonene fra køen.

Det aktive objektmønsteret spilte også en rolle i tidlig multi-tasking-systemer. Hvert objekt inne i et aktivt objekt vil holde en referanse til den aktive objekten. De ville utføre en del av jobbene deres og deretter sette seg tilbake i køen. Selv i dagens systemer kan du bruke et aktivt objekt til å la andre gjenstander fungere mens du venter på svar fra et annet program.


gjenbruk

Jeg er positiv at du har hørt det store løftet om objektorientert programmering: kodegenbruk. Tidlige vedtakere av OOP forutsatt bruk av universelle biblioteker og klasser i millioner av forskjellige prosjekter. Vel, det skjedde aldri.

Gjør noen malemetoder i stedet

Dette mønsteret muliggjør delvis gjenbruk av kode. Det er praktisk med flere algoritmer som bare er litt forskjellig fra hverandre.

Når: Eliminer duplisering på en enkel måte.

Hvorfor: Det er duplisering og fleksibilitet er ikke et problem.

Men fleksibilitet er fint. Hva om jeg virkelig trenger det?

Det er tid for en strategi

Når: Fleksibilitet og gjenbruk er viktigere enn enkelhet.

Hvorfor: Bruk den til å implementere store, utskiftbare biter av komplisert logikk, samtidig som du holder en vanlig algoritm signatur.

For eksempel kan du opprette en generisk Kalkulator og bruk deretter forskjellige ComputationStrategy objekter å utføre beregningene. Dette er et moderat brukt mønster, og det er mest kraftig når du må definere mange betingede oppføringer.


Oppdag-evne

Etter hvert som prosjekter vokser, blir det stadig vanskeligere for eksterne brukere å få tilgang til søknaden vår. Det er en grunn til å tilby et veldefinert inngangspunkt til søknaden eller modulen i spørsmålet. Andre slike grunner kan inkludere ønsket om å skjule modulens interne arbeid og struktur.

Presentere en fasade

En fasade er egentlig en API - et fint og klientvendt grensesnitt. Når en klient kaller en av disse fine metodene, delegerer fasaden en serie samtaler til klassene den skjuler for å gi klienten den nødvendige informasjonen eller ønsket resultat.

Når: For å forenkle API-en din eller forsettlig skjule indre forretningslogikk.

Hvorfor: Du kan kontrollere API og de virkelige implementeringer og logikk uavhengig.

Kontrollen er god, og mange ganger må du utføre en oppgave når noe endres. Brukere må bli varslet, røde lysdioder må blinke, en alarm må høres ... du får ideen.

Det populære Laravel-rammeverket gjør utmerket bruk av fasadmønsteret.

Abonner på en observatør

Et null objekt implementerer det samme grensesnittet som dine andre objekter.

Observatormønsteret gir en enkel måte å overvåke objekter på og ta handlinger når forholdene endres. Det finnes to typer observatør implementeringer:

  • Polling - Objekter aksepterer abonnenter. Abonnenter observerer objektet og blir varslet om bestemte hendelser. abonnenter spørre Observerte objekter for mer informasjon for å ta en handling.
  • Trykk - Som avstemningsmetoden aksepterer objekter abonnenter, og abonnenter blir varslet når en hendelse oppstår. Men når et varsel skjer, Observeren mottar også et hint som observatøren kan handle på.

Når: Å gi et varslingssystem inne i forretningslogikken din eller til omverdenen.

Hvorfor: Mønsteret gir en måte å kommunisere hendelser til et antall forskjellige objekter.

Bruk saker for dette mønsteret er e-postvarsler, logging-demoner eller meldingssystemer. Selvfølgelig er det i virkeligheten mange andre måter å bruke den på.

Koordinere effektene

Observatørmønsteret kan utvides med et mediator mønster. Dette mønsteret tar to objekter som parametere. Mediatoren abonnerer på den første parameteren, og når en endring skjer med det observerte objektet, bestemmer mediatoren hva som skal gjøres på den andre gjenstanden.

Når: De berørte objektene kan ikke vite om observerte objekter.

Hvorfor: Å tilby en skjult mekanisme for å påvirke andre objekter i systemet når ett objekt endres.


Singularity

Noen ganger trenger du spesielle objekter som er unike i søknaden din, og du vil sikre at alle forbrukere kan se noen endringer som er gjort i disse objektene. Du vil også forhindre at du oppretter flere forekomster av slike objekter av bestemte grunner, som lang initialiseringstid eller problemer med samtidige handlinger til noen tredjepartsbiblioteker.

Bruk en Singleton

En singleton er et objekt som har en privat konstruktør og et publikum getInstance () metode. Denne metoden sikrer at bare en forekomst av objektet eksisterer.

Når: Du trenger å oppnå singularitet og vil ha en kryssplattform, lazy evaluert løsning som også gir mulighet for opprettelse gjennom derivasjon.

Hvorfor: Å tilby et enkelt tilgangspunkt når det trengs.

Eller skriv et monostatobjekt

En annen tilnærming til singularitet er monostatedesignemønsteret. Denne løsningen bruker et triks som tilbys av objektorienterte programmeringsspråk. Den har dynamiske offentlige metoder som får eller angir verdiene for statiske private variabler. Dette sikrer i sin tur at alle forekomster av slike klasser deler de samme verdiene.

Når: Transparens, derivatbarhet og polymorfisme er foretrukket sammen med singularitet.

Hvorfor: For å skjule fra brukerne / klientene det faktum at objektet tilbyr singularitet.

Vær spesielt oppmerksom på singularitet. Det forurenser det globale navneområdet, og i de fleste tilfeller kan det erstattes med noe bedre egnet for den aktuelle situasjonen.


Kontrollere forskjellige objekter

Lagringsmønsteret er ganske nyttig for å implementere søkemetoder ...

Så du har en bryter og et lys. Bryteren kan slå lyset av og på, men nå har du kjøpt en fan og vil bruke den gamle bryteren med den. Det er lett å oppnå i den fysiske verden; ta bryteren, koble ledningene og violaen.

Dessverre er det ikke så lett i programmeringsverdenen. Du har en Bytte om klasse og a Lys klasse. Hvis din Bytte om bruker Lys, hvordan kunne den bruke Fan?

Lett! Kopier og lim inn Bytte om, og endre den for å bruke Fan. Men det er koden duplisering; det er det samme som å kjøpe en annen bryter for viften. Du kan utvide Bytte om til FanSwitch, og bruk det objektet i stedet. Men hva om du vil bruke en Knapp eller Fjernkontroll, i stedet for a Bytte om?

Det abstrakte servermønsteret

Dette er det enkleste mønsteret som noen gang er oppfunnet. Den bruker bare et grensesnitt. Det er det, men det er flere forskjellige implementeringer.

Når: Du må koble til objekter og opprettholde fleksibilitet.

Hvorfor: Fordi det er den enkleste måten å oppnå fleksibilitet, samtidig som man respekterer både avhengighetsinversjonsprinsippet og det åpne nærprinsippet.

PHP er skrevet dynamisk. Dette betyr at du kan utelate grensesnitt og bruke forskjellige objekter i samme kontekst - risikere en nektet erobring. Imidlertid tillater PHP også definisjonen av grensesnitt, og jeg anbefaler at du bruker denne flotte funksjonaliteten for å gi klarhet i hensikten med kildekoden din.

Men du har allerede en gruppe klasser du vil snakke med? Ja, selvfølgelig. Det er mange biblioteker, API'er fra tredjeparter og andre moduler som man må snakke med, men dette betyr ikke at vår forretningslogikk må vite detaljene til slike ting.

Sett inn en adapter

Adaptermønsteret skaper bare en korrespondanse mellom forretningslogikken og noe annet. Vi har allerede sett et slikt mønster i aksjon: gateway mønsteret.

Når: Du må opprette en tilkobling med en eksisterende og potensielt endringsmodul, et bibliotek eller en API.

Hvorfor: For å tillate at forretningslogikken din bare stole på de offentlige metodene, gir adapteren og tillater at den andre siden av adapteren enkelt skiftes.

Hvis noen av de ovennevnte mønstrene ikke passer med din situasjon, kan du bruke ...

Brosmønsteret

Dette er et veldig komplisert mønster. Jeg personlig ikke liker det fordi det vanligvis er lettere å ta en annen tilnærming. Men for de spesielle tilfellene, når andre løsninger feiler, kan du vurdere bromønsteret.

Når: Adaptermønsteret er ikke nok, og du endrer klasser på begge sider av røret.

Hvorfor: Å tilby økt fleksibilitet på bekostning av betydelig kompleksitet.


Sammensatt mønster

Vurder at du har et skript med lignende kommandoer, og du vil foreta en samtale for å kjøre dem. Vente! Visste vi ikke allerede noe slikt tidligere? Det aktive objektmønsteret?

Ja, det gjorde vi. Men denne er litt annerledes. Det er sammensatt mønster, og som det aktive objektmønsteret, holder det en liste over objekter. Men kaller en metode på et sammensatt objekt kaller den samme metoden på alle objekter uten å fjerne dem fra listen. Klientene som ringer en metode, tenker at de snakker med et enkelt objekt av den aktuelle typen, men faktisk blir deres handlinger brukt på mange, mange gjenstander av samme type.

Når: Du må søke en handling på flere lignende objekter.

Hvorfor: For å redusere duplisering og forenkle hvordan lignende objekter blir kalt.

Her er et eksempel: du har et program som er i stand til å lage og plassere Bestillinger. Anta at du har tre ordrer: $ bestilling1, $ order2 og $ order3. Du kan ringe sted() på hver av dem, eller du kan inneholde disse ordrene i en $ compositeOrder objekt, og ring det sted() metode. Dette kalles i sin tur sted() metode på alle de inneholdt Rekkefølge objekter.


Statens mønster

Gateways henter bare og vedvarer rå data.

En finite state machine (FSM) er en modell som har et begrenset antall diskrete tilstander. Implementering av en FSM kan være vanskelig, og den enkleste måten å gjøre dette innebærer den tillitsfulle bytte om uttalelse. Hver sak setningen representerer en nåværende tilstand i maskinen, og den vet hvordan du aktiverer neste tilstand.

Men det vet vi alle bytt ... saken uttalelser er mindre ønskelige fordi de produserer en uønsket høy fan-out på våre objekter. Så glem det bytt ... saken setning, og i stedet vurdere statsmønsteret. Statens mønster består av flere objekter: et objekt å koordinere ting, et grensesnitt som representerer en abstrakt tilstand, og deretter flere implementeringer - en for hver stat. Hver stat vet hvilken stat som kommer etter det, og staten kan varsle det koordinerende objektet for å sette sin nye stat til den neste på linjen.

Når: FSM-lignende logikk er nødvendig for å bli implementert.

Hvorfor: Å eliminere problemene med a bytt ... saken setning, og for bedre å inkapslere betydningen av hver enkelt stat.

En mat dispenser kan ha en hoved- klasse som har en referanse til a stat klasse. Mulige tilstandsklasser kan være noe som: WaitingForCoin, InsertedCoin, SelectedProduct, Venter på bekreftelse, DeliveringProduct, ReturningChange. Hver stat utfører sin jobb og oppretter neste tilstandsobjekt for å sende til koordinatorklassen.


Dekorer med dekoratormønsteret

Det er tider når du distribuerer klasser eller moduler gjennom et program, og du kan ikke endre dem uten å påvirke systemet radikalt. Men samtidig må du legge til ny funksjonalitet som brukerne trenger.

Dekoratormønsteret kan hjelpe i disse situasjonene. Det er veldig enkelt: ta eksisterende funksjonalitet og legg til det. Dette oppnås ved å utvide den opprinnelige klassen og gi ny funksjonalitet på kjøretid. Gamle kunder fortsetter å bruke det nye objektet som de ville være gamle, og nye kunder vil bruke både den gamle og den nye funksjonaliteten.

Når: Du kan ikke endre gamle klasser, men du må implementere ny oppførsel eller tilstand.

Hvorfor: Det gir en ubøyelig måte å legge til ny funksjonalitet.

Et enkelt eksempel er utskrift av data. Du skriver ut noen informasjon til brukeren som ren tekst, men du vil også gi muligheten til å skrive ut i HTML. Dekoratormønsteret er en slik løsning som lar deg beholde begge funksjonene.

Eller godta en besøkende

Hvis problemet med å utvide funksjonaliteten er annerledes - si, du har en komplisert treaktig struktur av objekter, og du vil legge til funksjonalitet for mange noder samtidig - det er ikke mulig å lage en enkel iterasjon, men en besøkende kan være en levedyktig løsning. Ulempen er imidlertid at en gjennomføring av besøkende mønster krever modifikasjon til den gamle klassen dersom den ikke var utformet for å godta en besøkende.

Når: En dekoratør er ikke hensiktsmessig og noe ekstra kompleksitet er akseptabelt.

Hvorfor: Å tillate og organisert tilnærming til å definere funksjonalitet for flere objekter, men til prisen av høyere kompleksitet.


Konklusjon

Bruk designmønstre for å løse dine problemer, men bare hvis de passer.

Designmønstre bidrar til å løse problemer. Som en gjennomføringsanbefaling må du aldri nevne klassene dine etter mønstrene. I stedet finner du de riktige navnene til de riktige abstraksjonene. Dette hjelper deg med å skille bedre når du virkelig trenger et mønster i motsetning til bare å implementere en fordi du kan.

Noen kan si at hvis du ikke nevner klassen din med mønsterets navn i den, vil andre utviklere ha en vanskelig tid å forstå koden din. Hvis det er vanskelig å gjenkjenne et mønster, er problemet i mønsterets implementering.

Bruk designmønstre for å løse dine problemer, men bare hvis de passer. Ikke misbruk dem. Du vil oppdage at en enklere løsning er et lite problem; mens du vil oppdage at du bare trenger et mønster etter at du har implementert noen andre løsninger.

Hvis du er ny for å designe mønstre, håper jeg at denne artikkelen har gitt deg en ide om hvordan mønstre kan være nyttige i dine applikasjoner. Takk for at du leste!