mboost-dp1

Java Command Design Pattern


Gå til bund
Gravatar #1 - squad2nd
26. jul. 2010 19:51
Nogen der er hajer til design patterns, specielt Command?

Et eksempel...

/* The Invoker class */
public class Switch {

private Command flipUpCommand;
private Command flipDownCommand;

public Switch(Command flipUpCmd, Command flipDownCmd) {
this.flipUpCommand = flipUpCmd;
this.flipDownCommand = flipDownCmd;
}

public void flipUp() {
flipUpCommand.execute();
}

public void flipDown() {
flipDownCommand.execute();
}
}

/* The Receiver class */
public class Light {

public Light() { }

public void turnOn() {
System.out.println("The light is on");
}

public void turnOff() {
System.out.println("The light is off");
}
}

/* The Command interface */
public interface Command {
void execute();
}


/* The Command for turning on the light */
public class FlipUpCommand implements Command {

private Light theLight;

public FlipUpCommand(Light light) {
this.theLight=light;
}

public void execute(){
theLight.turnOn();
}
}

/* The Command for turning off the light */
public class FlipDownCommand implements Command {

private Light theLight;

public FlipDownCommand(Light light) {
this.theLight=light;
}

public void execute() {
theLight.turnOff();
}
}

/* The test class or client */
public class PressSwitch {

public static void main(String[] args) {
Light lamp = new Light();
Command switchUp = new FlipUpCommand(lamp);
Command switchDown = new FlipDownCommand(lamp);

// See criticism of this model above:
// The switch itself should not be aware of lamp details (switchUp, switchDown)
// either directly or indirectly
Switch s = new Switch(switchUp,switchDown);

try {
if (args[0].equalsIgnoreCase("ON")) {
s.flipUp();
} else if (args[0].equalsIgnoreCase("OFF")) {
s.flipDown();
} else {
System.out.println("Argument \"ON\" or \"OFF\" is required.");
}
} catch (Exception e){
System.out.println("Arguments required.");
}
}
}


For at kunne bruge Undo/Redo funktionalitet i et program jeg er ved at lave, er jeg faldet over Command.
Jeg er dog lidt forvirret. Jeg forstår at hver eneste klasse som implementerer Command, har mulighed for at fx gemme den action og 'state' som skal til for at gengive den på et andet tidspunkt (fx i forbindelse med undo), MEN... Command kalder kun en metode, Execute() der ikke returnerer noget.

Mit spørgsmål er, hvad er den bedste måde at få et resultat tilbage på ved at kalde en void Execute() metode på et Command object?

Indtil videre har jeg brugt callback (ligesom i eksemplet øverst), dvs. inkluderet det object hvis states skal opdateres i forbindelse med et kald til Execute().

Jeg er synes dog det er lidt overkill, at man først skulle kalde Execute() ... og dernæst 'staten' på det object som Execute() skulle ændre... og noget andet er, hvad med Exceptions?

Mit problem er, at jeg har Actions, der implementerer Command, men som skal returnere et object.
Gravatar #2 - arne_v
26. jul. 2010 20:08
#1

Du vil gerne have en forklaring til Wikipedia koden?

:-)
Gravatar #3 - arne_v
26. jul. 2010 20:12
#1

Det korte svar er vel at den state gemmes i instansen af den konkrete command klasse.
Gravatar #4 - arne_v
26. jul. 2010 20:14
#1

For mere info end Wikipedia se f.eks.:

http://www.javaworld.com/javaworld/javatips/jw-jav...
http://geekswithblogs.net/davenet/articles/101320....

Det sidste viser noget UnDo og ReDo.
Gravatar #5 - Corholio
26. jul. 2010 20:18
arne_v (3) skrev:
#1

Det korte svar er vel at den state gemmes i instansen af den konkrete command klasse.


Det ville jeg også mene ville være en lige til måde at implementere det på.

squad2nd (1) skrev:
Mit problem er, at jeg har Actions, der implementerer Command, men som skal returnere et object.


Umiddelbart ser jeg ingen grund til at Commands skal returnere noget objekt, da operationen (som Command'en udfører) er indeholdt i Command-objektet selv. Da du giver objektet med som Command-objektet opererer på, kan du blot efterfølgende kigge på dets tilstand.

For at få "Undo" og "Redo" implementeret, er det vel blot at udvide Command-objektet med tilsvarende metoder (hvor redo vil kalde execute-metoden, og undo fører objektet tilbage til dets oprindelige tilstand).
Gravatar #6 - squad2nd
26. jul. 2010 21:32
#5

Da du giver objektet med som Command-objektet opererer på, kan du blot efterfølgende kigge på dets tilstand.


Det var det jeg tænkte.


Teoretisk: Hvis jeg har et object som har en metode som returnerer en ArrayList fx fra en database af nogen personer, så kræver det vel at jeg er nødt til at gemme en reference af den ArrayList på selve objektet, istedet for bare at returnere den med det samme?

Dvs. sådan noget ala det nedenstående, er ikke muligt ved at bruge Command?


interface Command
{ void execute() }


public class GetDataFromDatabase implements Command
{
private DatabaseList dbList;

public GetDataFromDatabase(DatabaseList g)
{
dbList = g;
}

public void execute()
{
dbList.getSomeDataFromDatabase();
}

}


public class DatabaseList
{
public ArrayList getSomeDataFromDatabase()
{
ArrayList f = //connect to database, and get some database
return f;
}



// Eller...
private ArrayList dataFromDb;
public void getSomeDataFromDatabase()
{
this.dataFromDb = //connect to database, and get some database
}

public ArrayList getDbData()
{
return dataFromDb;
}



}



Som I nok kan høre, er jeg lidt forvirret.




Gravatar #7 - Corholio
26. jul. 2010 21:43
#6

Jamen, hov hov....

Så vidt jeg forstår brugen af Command pattern er det til at enkapsulere en operation der foretager en ændring, derfor giver det også mening at lave en "undo" på denne operation.

F.eks, en Command der gemmer et tilføjer et objekt (eller rettere sagt, en objekt-til-relationel mapping af selv samme - hvis vi taler om databaser), når man så undo'er, "slettes" objektet.

Der er dog ingen måde til at undo operationen at "hente data", derfor vil jeg ikke mene at brugen af Command-pattern passer til det at populere en liste-ADT.

Men der er min generelle forståelse, samt (min pragmatiske) brug af Command-pattern.
Gravatar #8 - Corholio
27. jul. 2010 07:54
Har lige læst min post #7 igennem, og må indrømme at jeg ikke har den fjerneste anelse om hvad:

Der er dog ingen måde til at undo operationen at "hente data"


betyder?
Gravatar #9 - squad2nd
27. jul. 2010 14:30
#7

Jeg tænkte nok at man kun kunne kalde en metode og ikke få noget returneret igen.

Grunden til at jeg er forvirret er fordi at jeg er ved at bruge det interface i forbindelse med Swing, og dér har jeg givet en klasse til hver ActionListener. Altså fx:

class OpenFileDialog implements ActionListener


... men der er vel ingen grund til at bruge et Command interface når allerede "er" et ActionListener interface tilstede? Så kunne man vel oprette en UndoList med hver ActionListener der er blevet kaldt, eller hvad?

(jeg ved godt at Swing har sin egen UndoManager man kan bruge, men jeg tænkte jeg ville lære lidt low-level først)

Gravatar #10 - Corholio
27. jul. 2010 14:39
Jeg tror du forveksler interfaces med patterns.

Et command pattern kan ikke sammenlignes med en action listener. At signaturen for et command pattern kan minde om det fra en action listener, gør dem ikke til de samme.

Jeg vil umiddelbart fraråde dig at bruge Listeners som Command-objekter, så hellere lave en implementation af UndoableEdit (eller AbstractUndoableEdit), da disse er møntet på lige netop Command-pattern.

Hvis du vil have yderligere seperation, ville jeg dog lave mit eget Command interface (da det kun ville indeholde 1-3 metoder er det nok lige til at overkomme), og så bruge Adapter pattern til at få dette til at passe ind i Swing's arkitektur.
Dette vil yderligere give dig muligheden for at skifte valg af UI framework, hvis du senere ville kigge på SWT eller webbaserede teknologier (GWT heriblandt)
Gravatar #11 - squad2nd
27. jul. 2010 15:17
#10

Et command pattern kan ikke sammenlignes med en action listener.


Kan være du har ret, og forfatteren ikke er klog nok, men
ifølge dette her dokument om Command pattern:

But there are still a couple of more ways to approach this. If you give every control its own actionListener class, you are in effect creating individual command objects for each of them. And, in fact, this is really what the designers of the Java 1.1 event model had in mind.


Mit problem er egenligt Swing. Hvis du gider, og har lyst, vil jeg være meget taknemmelig hvis du kunne skrive et lille eksempel til mig i Java... bare et simpelt program hvor man trykker på en jbutton og den så kalder et Command object.
Gravatar #12 - squad2nd
27. jul. 2010 15:21
#10

Den eneste måde jeg selv lige kan finde ud af at implementere det på, er via:

class CutToClipboard implements ActionListener, Command


... men jeg ved sgu ikke om det er best practice.
Gravatar #13 - squad2nd
27. jul. 2010 15:38
Eller noget ala...

jButton1.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {
Command c = new CutCommand(jTextArea1);
c.execute();
}

});



Enten som en anonymous class eller en ActionListener for sig.
Gravatar #14 - arne_v
27. jul. 2010 15:53
#13

Command pattern giver mening, når det er forskellig kode der opretter command og udfører command.
Gravatar #15 - squad2nd
27. jul. 2010 16:00
#14

Så det er faktisk ikke et krav at bruge Command bare for at bruge alm. Undo/Redo funktionalitet.
Gravatar #16 - arne_v
27. jul. 2010 16:59
#15

Nej.

Command er for at decouple creator og invoker.

UnDo/ReDo er et eksempel blandt mange på funktionalitet der kan laves med Command.
Gravatar #17 - Corholio
27. jul. 2010 17:09
squad2nd (11) skrev:
#10

Kan være du har ret, og forfatteren ikke er klog nok, men
ifølge dette her dokument om Command pattern:

But there are still a couple of more ways to approach this. If you give every control its own actionListener class, you are in effect creating individual command objects for each of them. And, in fact, this is really what the designers of the Java 1.1 event model had in mind.



Jeg synes ikke de 2 udtalelser modsiger hinanden.

Min pointe er at en listener reagerer på events (og er UI og API specifik - oftest), en Command er pattern der eksekveres på baggrund af en event.

squad2nd (11) skrev:

Mit problem er egenligt Swing. Hvis du gider, og har lyst, vil jeg være meget taknemmelig hvis du kunne skrive et lille eksempel til mig i Java... bare et simpelt program hvor man trykker på en jbutton og den så kalder et Command object.


Jeg prøver lige at hacke noget sammen, men hvis det ikke virker, vil jeg undskylde det med det faktum at jeg har befundet mig i SWT-verdenen de sidste 5 år, sorry:

// Listener-registration etc.

JTextArea jTextArea1 = ….
UndoManager undoMgr = ...

jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
UndoableEdit adapter = new CommandAdapter(new CutCommand(jTextArea1));
undoMgr.undoableEditHappened(new UndoableEditEvent(e.getSource(), adapter);
}
});

// Interfaces and Classes, for Command(s) and "Swing"-API-wrapper

interface Command {
void execute();
void undo();
}

class CutCommand implements Command {
… field members…
public CutCommand(JTextArea area) {
this.area = area;
this.selection = area.getSelection(); // whatever the method is named
this.contents = area.getText();
}
void execute() {
// put selection on Clipboard and remove from area
}
void undo() {
// restore contents of area, and remove selection from Clipboard
}
}

class CommandAdapter extends AbstractUndoableEdit {
… field members…
public CommandAdapter(Command command) {
this.command = command;
}
public void undo() throws CannotUndoException() {
command.undo();
}
public void redo() throws CannotRedoException() {
command.redo();
}
… canUndo, and canRedo-methods goes here…
}

Gravatar #18 - squad2nd
27. jul. 2010 20:20
#17

Tak for eksemplet.
Ikke for at snage, men hvilke dele i Java har du programmeret mest i de sidste par år?
Gravatar #19 - Corholio
27. jul. 2010 20:47
Jeg har primært udviklet professionelt i UI / Application layer af Java applikationer på Desktop området. Heriblandt alt fra simple UI layouts, til mere komplekse grafiske vektor-baserede editorer.

På fritidsbasis er jeg mere interesseret i multitier applikationer, og roder med GWT, Web (HTML, CSS), PHP, Grails, diverse Java web frameworks (Struts...yuk, og Spring...knap så yuk) og andet i den retning.
Gå til top

Opret dig som bruger i dag

Det er gratis, og du binder dig ikke til noget.

Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.

Opret Bruger Login