Java, spara undan funktionsanrop i en map

Permalänk
Medlem

Java, spara undan funktionsanrop i en map

Hej på er!

Har ett litet problem. Det är så här att jag bygger ett program med många knappevents. För att göra det hela smidigt och lättarbetat har jag en ActionPreforme som tar emot eventen. Nu är det bara så att jag vill göra om den på ett snyggare sätt och minska ned koden då jag kör en väääldigt stor if, else if sats som kollar alla actioncommands som kommer in.

Min tanke har varit att lägga alla actioncommands i en map tillsammans med dess funktionsanrop (det som skall hända). Hur sjutton får jag till detta?

Har googlat länge och väl, försökt det ena och det andra men får inte riktigt till det. Så som jag vill ha det är typ:

public void ActionPreforme(ActionEvent e) { myMap.get(e.getActionCommand.toString()); }

Är det möjligt?

Permalänk
Medlem

Det går, men jag vet inte om det skulle göra din kod tydligare. Ibland är en episk if-else både mer lättläst och lättunderhållen än det du vill åstadkomma, även om det blir mer kod på samma ställe.

Hur som helst. Skapa ett interface, Command, som har en metod, execute(), enligt:

public interface Command { void execute(); }

Sedan kan du skapa en hel räcka klasser som implementerar detta interface. Varje sådan klass representerar en typ av åtgärd som du vill utföra i din actionPerformed() (som den väl heter).
Säg att vi vill ändra bakgrundsfärg på någon JPanel när vi trycker på någon av två knappar, en som ändrar till blå färg och en som ändrar till röd. Deras kommandoklasser skulle kunna se ut så här:

class BlueButtonCommand implements Command { private JPanel affectedPanel; BlueButtonCommand(JPanel affectedPanel) { this.affectedPanel = affectedPanel; } void execute() { affectedPanel.setBackground(Color.BLUE); } } class RedButtonCommand implements Command { private JPanel affectedPanel; RedButtonCommand(JPanel affectedPanel) { this.affectedPanel = affectedPanel; } void execute() { affectedPanel.setBackground(Color.RED); } }

Sedan kan du koppla instanser av dessa klasser till knapptryckningshändelsen genom att:

1) ha en Map<Object, Command> som kopplar en komponent till ett kommando, säg att den heter commandMap.

2) lägga in varje komponent och dess kommando i denna map, som såhär:

JButton blueButton = new JButton("Make it blue!"); commandMap.put(blueButton, new BlueButtonCommand(aPanel)); JButton redButton = new JButton("Make it red!"); commandMap.put(redButton, new RedButtonCommand(aPanel));

3) i actionPerformed plocka ut event-källan och använda den som nyckel till din commandMap:

public void actionPerformed(ActionEvent e) { commandMap.get(e.getSource()).execute(); }

Detta angreppssätt har klassats som ett av de vanliga "designmönstren", och fått just namnet Command. Exemplet ovan är egentligen mest löjligt, men det illustrerar förhoppningsvis hur det kan gå till.

I min mening finns det ganska sällan anledning till att använda det här i ett fall som ditt. Visst, din actionPerformed() blir jäkligt kompakt och elegant, men istället får du en massa andra klasser att hålla reda på, och det är för en som läser koden svårare att få reda på vad som egentligen händer när man trycker på en viss knapp.

Men det är ju samtidigt alltid kul att testa ett nytt sätt att jobba på också...

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av badboll
Det går, men jag vet inte om det skulle göra din kod tydligare. Ibland är en episk if-else både mer lättläst och lättunderhållen än det du vill åstadkomma, även om det blir mer kod på samma ställe.

Hur som helst. Skapa ett interface, Command, som har en metod, execute(), enligt:

public interface Command { void execute(); }

Sedan kan du skapa en hel räcka klasser som implementerar detta interface. Varje sådan klass representerar en typ av åtgärd som du vill utföra i din actionPerformed() (som den väl heter).
Säg att vi vill ändra bakgrundsfärg på någon JPanel när vi trycker på någon av två knappar, en som ändrar till blå färg och en som ändrar till röd. Deras kommandoklasser skulle kunna se ut så här:

class BlueButtonCommand implements Command { private JPanel affectedPanel; BlueButtonCommand(JPanel affectedPanel) { this.affectedPanel = affectedPanel; } void execute() { affectedPanel.setBackground(Color.BLUE); } } class RedButtonCommand implements Command { private JPanel affectedPanel; RedButtonCommand(JPanel affectedPanel) { this.affectedPanel = affectedPanel; } void execute() { affectedPanel.setBackground(Color.RED); } }

Sedan kan du koppla instanser av dessa klasser till knapptryckningshändelsen genom att:

1) ha en Map<Object, Command> som kopplar en komponent till ett kommando, säg att den heter commandMap.

2) lägga in varje komponent och dess kommando i denna map, som såhär:

JButton blueButton = new JButton("Make it blue!"); commandMap.put(blueButton, new BlueButtonCommand(aPanel)); JButton redButton = new JButton("Make it red!"); commandMap.put(redButton, new RedButtonCommand(aPanel));

3) i actionPerformed plocka ut event-källan och använda den som nyckel till din commandMap:

public void actionPerformed(ActionEvent e) { commandMap.get(e.getSource()).execute(); }

Detta angreppssätt har klassats som ett av de vanliga "designmönstren", och fått just namnet Command. Exemplet ovan är egentligen mest löjligt, men det illustrerar förhoppningsvis hur det kan gå till.

I min mening finns det ganska sällan anledning till att använda det här i ett fall som ditt. Visst, din actionPerformed() blir jäkligt kompakt och elegant, men istället får du en massa andra klasser att hålla reda på, och det är för en som läser koden svårare att få reda på vad som egentligen händer när man trycker på en viss knapp.

Men det är ju samtidigt alltid kul att testa ett nytt sätt att jobba på också...

Aha, så det är med Command jag får lösa det helt enkelt. Tänkt implementera command för att köra undo funktionalitet men inte tänkt på det i detta avseendet. Blir en hel del att refactora. Stort tack för ditt genomgående svar. Nu har jag iaf en möjlig lösning om jag får tid att göra en så pass stor om byggnad. Snyggt.

Permalänk
Medlem

Varför ens ha mappen? Tycker det låter vettigare att du faktiskt kör ett command när en knapp trycks på, typ:

JButton jb = new JButton("Whatevs"); jb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Command c = new BlueButtonCommand(aPanel)); c.execute(); } });

Fast om du ska ha undo och så så lär du ju vilja lägga in kommandot i en lista också.

Edit-
Eller ja asså, lägga in det i en lista lär du ju ändå vilja göra, så du kan separera gui:t och logiken så lägger du kommandon på en kö som sedan i logik-delen betar av.

Visa signatur

I just love the fact that there is a global integer variable named 'i'. Just think, you will never need to declare your loop variable again!
To avoid collisions where a loop that uses 'i' calls another function that loops with 'i', be sure to stack 'i' and restore it when your function exits.