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 Mottaker
grensesnitt.) 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 Mottaker
og 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 henrette
på Kommando
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 om
s 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 denBytte om
tilsvarende fra forrige eksempel. Den lagrer det generiskeTransactionCommand
objekt i sin privatemyCommand
variabel. NårrunCommands ()
blir påkalt, kaller denhenrette( )
av det aktuelleTransactionCommand
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.