Designmønstre fremskynder ikke kun designfasen af et objektorienteret (OO) projekt, men øger også produktiviteten i udviklingsteamet og softwarens kvalitet. EN Kommandomønster er et objektadfærdsmønster, der giver os mulighed for at opnå fuldstændig afkobling mellem afsenderen og modtageren. (EN afsender er et objekt, der påberåber en operation, og en modtager er et objekt, der modtager anmodningen om at udføre en bestemt operation. Med afkobling, afsenderen har intet kendskab til Modtager
interface.) Udtrykket anmodning her henviser til den kommando, der skal udføres. Kommandomønsteret giver os også mulighed for at variere, hvornår og hvordan en anmodning bliver opfyldt. Derfor giver et kommandomønster os fleksibilitet såvel som udvidelse.
I programmeringssprog som C, funktionsmarkører bruges til at eliminere kæmpe switch-udsagn. (Se "Java Tip 30: Polymorfisme og Java" for en mere detaljeret beskrivelse.) Da Java ikke har funktionsmarkører, kan vi bruge kommandomønsteret til at implementere tilbagekald. Du ser dette i aktion i det første kodeeksempel nedenfor, kaldet Testkommando.java
.
Udviklere, der er vant til at bruge funktionsmarkører på et andet sprog, kan blive fristet til at bruge Metode
objekter i Reflection API på samme måde. For eksempel viser Paul Tremblett i sin artikel "Java Reflection" dig, hvordan du bruger Reflection til at gennemføre transaktioner uden at bruge switch-sætninger. Jeg har modstået denne fristelse, da Sun fraråder brug af Reflection API, når andre værktøjer, der er mere naturlige for Java-programmeringssproget, er tilstrækkelige. (Se Ressourcer for links til Tremblett's artikel og til Sun's Reflection tutorial-side.) Dit program bliver lettere at fejle og vedligeholde, hvis du ikke bruger Metode
genstande. I stedet skal du definere en grænseflade og implementere den i de klasser, der udfører den nødvendige handling.
Derfor foreslår jeg, at du bruger kommandomønsteret kombineret med Java's dynamiske indlæsnings- og bindingsmekanisme til at implementere funktionspegere. (For detaljer om Java's dynamiske indlæsnings- og bindingsmekanisme, se James Gosling og Henry McGilton's "The Java Language Environment - A White Paper", der er anført i Resources.)
Ved at følge ovenstående forslag udnytter vi polymorfismen leveret af anvendelsen af et kommandomønster til at eliminere kæmpe switch-udsagn, hvilket resulterer i udvidelige systemer. Vi udnytter også Javas unikke dynamiske indlæsnings- og bindingsmekanismer til at opbygge et dynamisk og dynamisk udvideligt system. Dette illustreres i det andet kodeeksempeleksempel nedenfor, kaldet TestTransactionCommand.java
.
Kommandomønsteret gør selve anmodningen til et objekt. Dette objekt kan gemmes og sendes rundt som andre objekter. Nøglen til dette mønster er en Kommando
interface, der erklærer en grænseflade til udførelse af operationer. I sin enkleste form inkluderer denne grænseflade et abstrakt udføre
operation. Hver beton Kommando
klasse angiver et modtager-handlingspar ved at gemme Modtager
som en instansvariabel. Det giver forskellige implementeringer af udføre ()
metode til at påkalde anmodningen. Det Modtager
har den nødvendige viden til at udføre anmodningen.
Figur 1 nedenfor viser Kontakt
- en sammenlægning af Kommando
genstande. Det har flipUp ()
og flipDown ()
operationer i dens grænseflade. Kontakt
kaldes påkalder fordi det påberåber sig udførelsesoperationen i kommandogrænsefladen.
Den konkrete kommando, LightOnCommand
, implementerer udføre
betjening af kommandogrænsefladen. Det har viden til at kalde det rette Modtager
objektets funktion. Det fungerer i dette tilfælde som en adapter. Ved udtrykket adapter, Jeg mener, at betonen Kommando
objektet er et simpelt stik, der forbinder Invoker
og Modtager
med forskellige grænseflader.
Klienten instantierer Invoker
, det Modtager
og de konkrete kommandoobjekter.
Figur 2, sekvensdiagrammet, viser interaktionerne mellem objekterne. Det illustrerer hvordan Kommando
afkobler Invoker
fra Modtager
(og den anmodning, den gennemfører). Klienten opretter en konkret kommando ved at parametrere sin konstruktør med det relevante Modtager
. Derefter gemmer den Kommando
i Invoker
. Det Invoker
kalder den konkrete kommando tilbage, som har viden til at udføre det ønskede Handling()
operation.
Klienten (hovedprogram i listen) skaber en beton Kommando
objekt og indstiller dens Modtager
. Som en Invoker
objekt, Kontakt
opbevarer betonen Kommando
objekt. Det Invoker
udsender en anmodning ved at ringe udføre
på den Kommando
objekt. Betonen Kommando
objekt påberåber operationer på dets Modtager
at udføre anmodningen.
Hovedideen her er, at den konkrete kommando registrerer sig selv med Invoker
og Invoker
kalder det tilbage og udfører kommandoen på Modtager
.
Eksempelkode for kommandomønster
Lad os se på et simpelt eksempel, der illustrerer tilbagekaldsmekanismen opnået via kommandomønsteret.
Eksemplet viser en Ventilator
og en Lys
. Vores mål er at udvikle en Kontakt
der kan tænde eller slukke for enten objekt. Vi ser, at Ventilator
og Lys
har forskellige grænseflader, hvilket betyder Kontakt
skal være uafhængig af Modtager
interface, eller den har intet kendskab til koden> Modtagerens interface. For at løse dette problem skal vi parametrere hver af Kontakt
s med den relevante kommando. Det er klart, at Kontakt
tilsluttet til Lys
har en anden kommando end Kontakt
tilsluttet til Ventilator
. Det Kommando
Klassen skal være abstrakt eller en grænseflade for at dette kan fungere.
Når konstruktøren til en Kontakt
påberåbes, parametriseres den med det relevante sæt kommandoer. Kommandoerne gemmes som private variabler af Kontakt
.
Når flipUp ()
og flipDown ()
operationer kaldes, de vil simpelthen foretage den passende kommando til udføre ()
. Det Kontakt
har ingen idé om, hvad der sker som et resultat af udføre ()
bliver kaldt.
TestCommand.java klasse Fan {public void startRotate () {System.out.println ("Fan roterer"); } public void stopRotate () {System.out.println ("Ventilator roterer ikke"); }} klasse Light {public void turnOn () {System.out.println ("Light is on"); } public void turnOff () {System.out.println ("Lyset er slukket"); }} klasse Skift {privat Kommando UpCommand, DownCommand; public Switch (Command Up, Command Down) {UpCommand = Op; // concrete Command registrerer sig hos invoker DownCommand = Down; } void flipUp () {// invoker kalder konkret Kommando tilbage, som udfører Kommandoen på modtageren UpCommand. udføre () } ugyldigt flipDown () {DownCommand. udføre () }} klasse LightOnCommand implementerer Command {private Light myLight; offentlig LightOnCommand (lys L) {myLight = L; } public void execute () {myLight. tænde for( ); }} klasse LightOffCommand implementerer Command {private Light myLight; offentlig LightOffCommand (lys L) {myLight = L; } public void execute () {myLight. sluk( ); }} 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); Skift testSwitch = ny switch (testLOC, testLFC); testSwitch.flipUp (); testSwitch.flipDown (); Fan testFan = ny ventilator (); FanOnCommand foc = ny FanOnCommand (testFan); FanOffCommand ffc = ny FanOffCommand (testFan); Switch ts = ny switch (foc, ffc); ts.flipUp (); ts.flipDown (); }} Command.java public interface Command {public abstract void execute (); }
Bemærk i kodeeksemplet ovenfor, at kommandomønsteret afkobler objektet, der påberåber operationen fuldstændigt - (Kontakt )
- fra dem, der har viden til at udføre det - Lys
og Ventilator
. Dette giver os stor fleksibilitet: objektet, der udsteder en anmodning, skal kun vide, hvordan man udsteder det; det behøver ikke at vide, hvordan anmodningen gennemføres.
Kommandomønster til implementering af transaktioner
Et kommandomønster er også kendt som et handling eller transaktionsmønster. Lad os overveje en server, der accepterer og behandler transaktioner leveret af klienter via en TCP / IP-stikforbindelse. Disse transaktioner består af en kommando efterfulgt af nul eller flere argumenter.
Udviklere bruger muligvis en switch-sætning med en sag til hver kommando. Anvendelse af Kontakt
udsagn under kodning er et tegn på dårligt design i designfasen af et objektorienteret projekt. Kommandoer repræsenterer en objektorienteret måde at understøtte transaktioner på og kan bruges til at løse dette designproblem.
I programmets klientkode TestTransactionCommand.java
, alle anmodninger er indkapslet i det generiske TransactionCommand
objekt. Det TransactionCommand
konstruktør oprettes af klienten, og den registreres hos CommandManager
. Forespørgslerne i kø kan udføres på forskellige tidspunkter ved at ringe til runCommands ()
, hvilket giver os en masse fleksibilitet. Det giver os også muligheden for at samle kommandoer i en sammensat kommando. jeg har også CommandArgument
, Kommandomodtager
og CommandManager
klasser og underklasser af TransactionCommand
- nemlig Tilføj kommando
og SubtractCommand
. Følgende er en beskrivelse af hver af disse klasser:
CommandArgument
er en hjælperklasse, der gemmer argumenterne for kommandoen. Det kan omskrives for at forenkle opgaven med at sende et stort eller variabelt antal argumenter af enhver type.Kommandomodtager
implementerer alle kommandobehandlingsmetoder og implementeres som et Singleton-mønster.CommandManager
er påkalderen og er denKontakt
svarende til det foregående eksempel. Det gemmer det generiskeTransactionCommand
objekt i sin privatemyCommand
variabel. HvornårrunCommands ()
påberåbes, kalder detudføre ()
af det relevanteTransactionCommand
objekt.
I Java er det muligt at slå definitionen af en klasse op, der får en streng, der indeholder dens navn. I udføre ()
drift af TransactionCommand
klasse beregner jeg klassens navn og linker det dynamisk til det kørende system - det vil sige, at klasser indlæses på farten efter behov. Jeg bruger navngivningskonventionen, kommandonavnet sammenkædet af strengen "Kommando" som navnet på transaktionskommandoens underklasse, så den kan indlæses dynamisk.
Bemærk, at Klasse
genstand returneret af newInstance ()
skal støbes til den passende type. Dette betyder, at den nye klasse enten skal implementere en grænseflade eller underklasse en eksisterende klasse, som er kendt af programmet på kompileringstidspunktet. I dette tilfælde, da vi implementerer Kommando
interface, dette er ikke et problem.