Programmering

Java Tips 68: Lær hvordan du implementerer kommandomønsteret i Java

Designmønstre akselererer ikke bare designfasen til et objektorientert (OO) prosjekt, men øker også produktiviteten til utviklingsteamet og programvarens kvalitet. EN Kommandomønster er et atferdsmønster som tillater oss å oppnå fullstendig frakobling mellom avsender og mottaker. (EN avsender er et objekt som påkaller en operasjon, og en mottaker er et objekt som mottar forespørselen om å utføre en bestemt operasjon. Med frakobling, avsenderen har ingen kjennskap til Mottakergrensesnitt.) Begrepet be om her refererer til kommandoen som skal utføres. Kommandomønsteret lar oss også variere når og hvordan en forespørsel blir oppfylt. Derfor gir et kommandomønster oss fleksibilitet så vel som utvidbarhet.

I programmeringsspråk som C, funksjonspekere brukes til å eliminere gigantiske bryteruttalelser. (Se "Java Tips 30: Polymorphism and Java" for en mer detaljert beskrivelse.) Siden Java ikke har funksjonspekere, kan vi bruke kommandomønsteret til å implementere tilbakeringinger. Du ser dette i aksjon i det første kodeeksemplet nedenfor, kalt TestCommand.java.

Utviklere som er vant til å bruke funksjonspekere på et annet språk, kan bli fristet til å bruke Metode objekter av Reflection API på samme måte. For eksempel, i sin artikkel "Java Reflection", viser Paul Tremblett deg hvordan du bruker Reflection til å implementere transaksjoner uten å bruke bryteruttalelser. Jeg har motstått denne fristelsen, siden Sun fraråder å bruke Reflection API når andre verktøy som er mer naturlige for Java-programmeringsspråket, vil være tilstrekkelig. (Se Ressurser for lenker til Trembletts artikkel og for Suns refleksjonsside.) Programmet ditt blir lettere å feilsøke og vedlikeholde hvis du ikke bruker Metode gjenstander. I stedet bør du definere et grensesnitt og implementere det i klassene som utfører den nødvendige handlingen.

Derfor foreslår jeg at du bruker kommandomønsteret kombinert med Javas dynamiske laste- og bindingsmekanisme for å implementere funksjonspekere. (For detaljer om Javas dynamiske laste- og bindingsmekanisme, se James Gosling og Henry McGiltons "The Java Language Environment - A White Paper", oppført i Resources.)

Ved å følge det ovennevnte forslaget utnytter vi polymorfismen gitt ved anvendelse av et kommandomønster for å eliminere gigantiske bryteruttalelser, noe som resulterer i utvidbare systemer. Vi utnytter også Javas unike dynamiske belastnings- og bindingsmekanismer for å bygge et dynamisk og dynamisk utvidbart system. Dette er illustrert i det andre kodeeksempeleksemplet nedenfor, kalt TestTransactionCommand.java.

Kommandomønsteret gjør selve forespørselen til et objekt. Dette objektet kan lagres og sendes rundt som andre gjenstander. Nøkkelen til dette mønsteret er en Kommando grensesnitt, som erklærer et grensesnitt for å utføre operasjoner. I sin enkleste form inkluderer dette grensesnittet et abstrakt henrette operasjon. Hver betong Kommando klasse spesifiserer et mottakerhandlingspar ved å lagre Mottaker som en instansvariabel. Det gir forskjellige implementeringer av henrette() metode for å påkalle forespørselen. De Mottaker har den kunnskapen som kreves for å utføre forespørselen.

Figur 1 nedenfor viser Bytte om - en aggregering av Kommando gjenstander. Det har snu opp() og flipDown () operasjoner i grensesnittet. Bytte om kalles innkaller fordi den påkaller utføringsoperasjonen i kommandogrensesnittet.

Den konkrete kommandoen, LightOnCommand, implementerer henrette drift av kommandogrensesnittet. Den har kunnskapen til å kalle det aktuelle Mottaker objektets operasjon. Det fungerer som adapter i dette tilfellet. Ved begrepet adapter, Jeg mener at betongen Kommando objektet er en enkel kontakt, som kobler til Invoker og Mottaker med forskjellige grensesnitt.

Klienten instantierer Invoker, den Mottakerog de konkrete kommandoobjektene.

Figur 2, sekvensdiagrammet, viser samspillet mellom objektene. Det illustrerer hvordan Kommando avkobler Invoker fra Mottaker (og forespørselen den gjennomfører). Klienten oppretter en konkret kommando ved å parametrisere konstruktøren med riktig Mottaker. Så lagrer den Kommando i Invoker. De Invoker kaller tilbake den konkrete kommandoen, som har kunnskapen til å utføre ønsket Handling() operasjon.

Klienten (hovedprogrammet i oppføringen) lager en betong Kommando objektet og setter sitt Mottaker. Som en Invoker gjenstand, Bytte om lagrer betongen Kommando gjenstand. De Invoker sender en forespørsel ved å ringe henretteKommando gjenstand. Betongen Kommando objekt påkaller operasjoner på sin Mottaker å utføre forespørselen.

Hovedideen her er at den konkrete kommandoen registrerer seg med Invoker og Invoker kaller det tilbake, utfører kommandoen på Mottaker.

Eksempelkode for kommandomønster

La oss se på et enkelt eksempel som illustrerer tilbakeringingsmekanismen oppnådd via kommandomønsteret.

Eksemplet viser en Fan og en Lys. Vårt mål er å utvikle en Bytte om som kan slå enten objekt på eller av. Vi ser at den Fan og Lys har forskjellige grensesnitt, som betyr Bytte om må være uavhengig av Mottaker grensesnitt eller det har ingen kjennskap til koden> Mottakerens grensesnitt. For å løse dette problemet, må vi parameterisere hver av Bytte oms med riktig kommando. Åpenbart, den Bytte om koblet til Lys vil ha en annen kommando enn Bytte om koblet til Fan. De Kommando klassen må være abstrakt eller et grensesnitt for at dette skal fungere.

Når konstruktøren for en Bytte om blir påkalt, blir den parameterisert med riktig sett med kommandoer. Kommandoene lagres som private variabler av Bytte om.

Når snu opp() og flipDown () operasjoner kalles, vil de ganske enkelt gi den riktige kommandoen til henrette( ). De Bytte om aner ikke hva som skjer som et resultat av henrette( ) blir kalt.

TestCommand.java-klasse Fan {public void startRotate () {System.out.println ("Viften roterer"); } public void stopRotate () {System.out.println ("Viften roterer ikke"); }} class Light {public void turnOn () {System.out.println ("Light is on"); } public void turnOff () {System.out.println ("Lyset er av"); }} class Switch {private Command UpCommand, DownCommand; public Switch (Command Up, Command Down) {UpCommand = Opp; // konkret kommando registrerer seg hos innkalleren DownCommand = Down; } void flipUp () {// invoker kaller tilbake konkret Command, som utfører Command på mottakeren UpCommand. henrette ( ) ; } ugyldig flipDown () {DownCommand. henrette ( ); }} klasse LightOnCommand implementerer Command {private Light myLight; offentlig LightOnCommand (lys L) {myLight = L; } offentlig tomrom utføre () {myLight. Slå på( ); }} klasse LightOffCommand implementerer Command {private Light myLight; offentlig LightOffCommand (lys L) {myLight = L; } offentlig tomrom utføre () {myLight. skru av( ); }} klasse FanOnCommand implementerer Command {private Fan myFan; offentlig FanOnCommand (Fan F) {myFan = F; } public void execute () {myFan. startRotate (); }} klasse FanOffCommand implementerer Command {private Fan myFan; offentlig FanOffCommand (Fan F) {myFan = F; } public void execute () {myFan. stopRotate (); }} offentlig klasse TestCommand {public static void main (String [] args) {Light testLight = new Light (); LightOnCommand testLOC = ny LightOnCommand (testLight); LightOffCommand testLFC = ny LightOffCommand (testLight); Bytt testSwitch = ny bryter (testLOC, testLFC); testSwitch.flipUp (); testSwitch.flipDown (); Fan testFan = ny Fan (); FanOnCommand foc = ny FanOnCommand (testFan); FanOffCommand ffc = ny FanOffCommand (testFan); Bryter ts = ny bryter (foc, ffc); ts.flipUp (); ts.flipDown (); }} Command.java public interface Command {public abstract void execute (); } 

Legg merke til i kodeeksemplet ovenfor at kommandomønsteret frakobler objektet som påkaller operasjonen - (Bytte om ) - fra de som har kunnskapen til å utføre det - Lys og Fan. Dette gir oss stor fleksibilitet: objektet som utsteder en forespørsel, må bare vite hvordan man skal utstede det; det trenger ikke å vite hvordan forespørselen vil bli utført.

Kommandomønster for å gjennomføre transaksjoner

Et kommandomønster er også kjent som et handling eller transaksjonsmønster. La oss vurdere en server som godtar og behandler transaksjoner levert av klienter via en TCP / IP-tilkobling. Disse transaksjonene består av en kommando, etterfulgt av null eller flere argumenter.

Utviklere kan bruke en bryteruttalelse med en sak for hver kommando. Bruk av Bytte om uttalelser under koding er et tegn på dårlig design under designfasen av et objektorientert prosjekt. Kommandoer representerer en objektorientert måte å støtte transaksjoner på og kan brukes til å løse dette designproblemet.

I klientkoden til programmet TestTransactionCommand.java, alle forespørslene er innkapslet i generikken TransactionCommand gjenstand. De TransactionCommand konstruktøren er opprettet av klienten og den er registrert hos CommandManager. Køforespørslene kan utføres på forskjellige tidspunkter ved å ringe runCommands (), som gir oss mye fleksibilitet. Det gir oss også muligheten til å samle kommandoer i en sammensatt kommando. jeg har også CommandArgument, Kommandomottaker, og CommandManager klasser og underklasser av TransactionCommand - nemlig Legg til kommando og SubtractCommand. Følgende er en beskrivelse av hver av disse klassene:

  • CommandArgument er en hjelperklasse, som lagrer argumentene til kommandoen. Det kan skrives om for å forenkle oppgaven med å sende et stort eller variabelt antall argumenter av hvilken som helst type.

  • Kommandomottaker implementerer alle kommandobehandlingsmetodene og er implementert som et Singleton-mønster.

  • CommandManager er påkaller og er den Bytte om tilsvarende fra forrige eksempel. Den lagrer det generiske TransactionCommand objekt i sin private myCommand variabel. Når runCommands () blir påkalt, kaller den henrette( ) av det aktuelle TransactionCommand gjenstand.

I Java er det mulig å slå opp definisjonen av en klasse gitt en streng som inneholder navnet. I henrette ( ) drift av TransactionCommand klasse, beregner jeg kursnavnet og kobler det dynamisk til det løpende systemet - det vil si at klasser lastes omgående etter behov. Jeg bruker navngivningskonvensjonen, kommandonavnet sammenkoblet av strengen "Kommando" som navnet på underklassen for transaksjonskommandoen, slik at den kan lastes dynamisk.

Legg merke til at Klasse gjenstand returnert av newInstance () må støpes til riktig type. Dette betyr at den nye klassen enten må implementere et grensesnitt eller underklasse en eksisterende klasse som er kjent for programmet på kompileringstidspunktet. I dette tilfellet, siden vi implementerer Kommando grensesnitt, dette er ikke et problem.

$config[zx-auto] not found$config[zx-overlay] not found