Kung Fu Commands: Shifu Teaches Po the Command Pattern with Java Functional Interfaces
Po skips training (again) but learns the Command Pattern from Shifu instead. Part of our "Design Patterns with Kung Fu" series. No dumplings were harmed.
Join the DZone community and get the full member experience.
Join For FreePo was supposed to be practicing the Dragon Scrolls. Instead, he was… well, eating.
Shifu (arms crossed): "Po! Again? Training time, not dumpling time."
Po (mouth full): "But master, the noodles were calling me. And… so was the steamed bun."
Shifu sighed. It was time to teach Po a lesson. Not with fists—but with code.
Shifu Reveals the Command Pattern
Shifu: "Po, what if you could encapsulate actions as objects? Delegate responsibility? Allow reordering, queuing, and undoing actions?"
Po: "You mean... like a dumpling ordering scroll?"
Shifu: "Exactly. That is the Command Pattern."
"Command Pattern encapsulates a piece of behavior (like a spell scroll) and lets you pass it around like an object." —OOGWAY
Shifu Explains: What is the Command Pattern?
Shifu (voice calm, focused):
"Po, the Command Pattern is a design pattern where we encapsulate a request as an object, allowing us to parameterize clients with different requests, queue or log requests, and support undoable operations.
Think of it like a scroll—each scroll has the instruction to do something. It can be passed, stored, delayed, or cancelled."
Core Scroll Roles:
Role | Meaning |
---|---|
Command | Declares an interface for executing an action |
ConcreteCommand | Implements the Command interface, binds a Receiver to an action |
Receiver | The object that knows how to perform the actual operation |
Invoker | Initiates the request |
Client | Creates the command and configures the receiver |
Po: "So... like a noodle scroll for making dumplings on command?"
Shifu: "Exactly. Even training can be scroll-ified."
OOP Version of Command Pattern (Scroll Style)
interface Command {
void execute();
}
class EatNoodlesCommand implements Command {
public void execute() {
System.out.println("Po eats noodles.");
}
}
class DoPushUpsCommand implements Command {
public void execute() {
System.out.println("Po does push-ups.");
}
}
class Invoker {
public void takeCommand(Command command) {
command.execute();
}
}
public class TrainingGround {
public static void main(String[] args) {
Invoker invoker = new Invoker();
invoker.takeCommand(new EatNoodlesCommand());
invoker.takeCommand(new DoPushUpsCommand());
}
}
Shifu: "Each command knows only one thing—how to do its job."
Po Tries Functional Kung Fu
Po: "Shifu, that's a lot of classes. Can’t we use… you know, Java 8 powers?"
Shifu (nods): "The time has come to learn the Lambda Palm Strike."
Functional Java Version using Runnable
and Consumer
package com.javaonfly.designpatterns.command.fp;
import java.util.function.Consumer;
public class FunctionalTrainingGround {
public static void execute(Runnable command) {
command.run();
}
public static <T> void executeCommand(T data, Consumer<T> command) {
command.accept(data);
}
public static void main(String[] args) {
// Runnable-style Command
execute(() -> System.out.println("Po jumps over a log."));
// Consumer-style Command
executeCommand("Po", name -> System.out.println(name + " practices high kicks."));
}
}
What’s Happening Here?
Functional Command
Runnable
is a functional interface:void run()
— perfect for commands without inputs.Consumer<T>
is also functional:void accept(T t)
— great for commands that act on something.
Why This is the Command Pattern
- We’re encapsulating logic (commands) as functions.
- Passing behavior (commands) into the
execute()
method. - Easy to queue, delay, or undo.
Real-Life Example
SmartHome Command Queue (Functional + Generic)
package com.javaonfly.designpatterns.command.fp;
import java.util.List;
import java.util.*;
import java.util.function.Consumer;
public class SmartHome {
public static <T> void processCommands(List<T> items, List<Consumer<T>> commands) {
for (Consumer<T> command : commands) {
for (T item : items) {
command.accept(item);
}
}
}
public static void main(String[] args) {
List<String> devices = Arrays.asList("Lights", "Coffee Machine", "Music Player");
List<Consumer<String>> commands = Arrays.asList(
device -> System.out.println("Turn on " + device),
device -> System.out.println("Check status of " + device),
device -> System.out.println("Turn Off " + device)
);
processCommands(devices, commands);
}
}
//Output
Turn on Lights
Turn on Coffee Machine
Turn on Music Player
Check status of Lights
Check status of Coffee Machine
Check status of Music Player
Turn Off Lights
Turn Off Coffee Machine
Turn Off Music Player
Po Explains processCommands
to Shifu – Like a True Warrior of Code
"Master Shifu, behold! I have mastered the art of functional command execution using the processCommands
method. Allow me to explain, step by step..."
I make the method generic using <T>
, which means I can pass any type—like String
for device names or even my secret dumpling stash!
I give it two scrolls: one has the list of items (like smart devices), and the other has the list of commands (like turn on, check status, turn off). These are functions written in the form of Consumer<T>
—basically actions!
Loop over each command in the list. Each command is like a kung fu move stored in a scroll. We’re preparing to apply them to every device.
Then, for each command, I go over each item in the list. Like using the same move on every opponent in the training hall!
Here’s the heart of the execution! accept()
means do the thing. The command gets the item and performs its duty—like shouting ‘WATAH!’ while turning on the lights.
"Shifu, I've learned that real mastery is not in how many buns I can eat, but how many behaviors I can compose and execute with elegance. And maybe a bit of sesame oil."
Wisdom of the Pattern
- Behavior as Data: Commands are passed as data, in the form of lambda expressions.
- Decoupling: The processor doesn't know what devices are or what commands do. It just executes.
- Flexibility: Easily add, remove, or change commands. Add more devices. No change to logic!
Shifu's Final Words
Shifu: "You see, Po, sometimes… eating is a command. Training is a command. And your destiny… is one you must execute yourself."
Po (grinning): "Then I’ll queue my dumpling break after my code kata!"
Published at DZone with permission of Shaamik Mitraa, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments