การเขียนโปรแกรมในรูปแบบ command มีจุดประสงค์เพื่อแยกส่วนที่เป็นวิธีการทำงาน (business logic) ออกจากการเรียกใช้งาน หลักการคือซ่อนองค์ประกอบของสิ่งที่เกี่ยวข้องกับวิธีการทำไม่ว่าจะเป็นเมธอด พารามิเตอร์ หรือออบเจกต์ไว้ในออบเจกต์เดียว ส่วนการเรียกใช้งานจะทำผ่านอีกออบเจกต์หนึ่งซึ่งจะทำหน้าที่แค่เรียกใช้เมธอดที่กำหนดมาเพื่อทำงานนั้นๆเท่านั้น ออบเจกต์ที่เก็บส่วนที่เป็นวิธีการทำงานเราเรียกว่า receiver ส่วนออบเจกต์ที่เป็นคำสั่งจะแบ่งออกเป็น 2 ส่วนคือออบเจกต์ส่วนที่เป็นตัวคำสั่งซึ่งจะไม่มีการดำเนินการใดๆ (no business logic ) เรียกว่า command และออบเจกต์ที่เรียกใช้คำสั่งเรียกว่า invoke

ประโยชน์ของการเขียนโปรแกรมในรูปแบบนี้คือ เมื่อต้องมีการแก้ไขวิธีการทำงานก็จะทำการแก้ไขที่ receiver ที่เดียว ดังนั้นจะไม่กระทบกับโปรแกรมที่เรียกใช้งาน และสามารถแยกส่วนของการเรียกใช้คำสั่งออกจากส่วนของวิธีการทำงานได้อย่างเด็ดขาด

จากตัวอย่างด้านล่างจะเป็นรูปแบบการเขียนโปรแกรมแบบ command เพื่อสั่ง เปิด/ปิด หลอดไฟเริ่มจากการประกาศอินเตอร์เฟส Command ซึ่งจะมีเมธอดเดียวคือ execute() วัตถุประสงค์เพื่อเป็นกรอบของการเรียกใช้คำสั่ง (framework) เพื่อเอาไว้เรียกเมธอดที่ต้องการมาทำงานโดยต้องไม่มีรายละเอียดวิธีการทำงาน (business logic) อยู่ในคลาสนี้ นอกจากการใช้อินเตอร์เฟส เราอาจจะใช้คลาสแบบ abstarct หรือ คลาสธรรมดาก็ได้ แต่ให้ยึดตามวัตถุประสงค์

public interface Command {
    void execute();
}

จากนั้นจึงประกาศคลาสที่เป็น concrete command คือ  LightOnCommand และ LightOffCommand ซึ่งสืบทอดมาจากอินเตอร์เฟส Command วัตถุประสงค์เพื่อใช้เป็นคำสั่งสำหรับเรียกใช้งานโดยซ่อนส่วนที่ติดต่อกับวิธีการทำงานจริงๆเอาไว้โดยการกำหนดฟิลด์ที่เก็บออบเจกต์ของ receiver ให้เป็นแบบ private ส่วนตัวคำสั่งหรือเมธอดที่จะให้เรียกใช้งานได้จะกำหนดเป็นแบบ public และโอเวอร์ไรด์เมธอด execute() เพื่อเรียกใช้เมธอดที่เป็นวิธีการทำงานจริงๆของ receiver ซึ่งทั้งคลาส command คลาส LightOnCommand และ LightOffCommand เรารียกว่าส่วน command

public class LightOnCommand implements Command {

    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.lightOn();
    }
}

public class LightOffCommand implements Command {

    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.lightOff();
    }
}

ถัดมาคือการประกาศคลาส Light ซึ่งจะเป็นคลาสที่ระบุวิธีการทำงานที่เกิดขึ้นจริงๆ (business logic) เมื่อมีการเรียกใช้เมธอด ซึ่งคลาส Light คือส่วนที่เราเรียกว่า receiver

public class Light {

    public void lightOn() {
        System.out.println(“Turn on Light”);
    }

    public void lightOff() {
        System.out.println(“Turn off Light”);
    }
}

และสุดท้ายคือการสร้างคลาสที่เป็นส่วนที่เรียกใช้คำสั่ง (invoke) ซึ่งเป็นคลาสที่จะกำหนดว่าจะเรียกใช้คำสั่งอย่างไร

public class Controller {

    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void executeCommand() {
        command.execute();
    }
}

ในการใช้งานจะเป็นการใช้งานผ่านคลาสที่เป็นส่วนที่เรียกใช้คำสั่ง (invoke) ซึ่งสามารถสรุปขั้นตอนการใช้งานได้ง่ายๆว่า 1. สร้างออบเจกต์ของคลาสที่เป็นส่วนที่เรียกใช้คำสั่งซึ่งในที่นี้คือคลาส Controller 2. สร้างคำสั่งโดยระบุชนิดของออบเจกต์เป็น command และสร้างออบเจกต์ที่คำสั่งเฉพาะของแต่ละงาน และ 3. เรียกใช้คำสั่งผ่านออบเจกต์ invoke

public class HomeAutomationDemo {

    public static void main(String[] args) {

        Controller controller = new Controller();
        Light light = new Light();

        Command lightsOn = new LightOnCommand(light);
        Command lightsOff = new LightOffCommand(light);

        controller.setCommand(lightsOn);
        controller.executeCommand();

        controller.setCommand(lightsOff);
        controller.executeCommand();
    }
}

จากรูปด้านบน ด้านซ้ายเป็นการเรียกใช้ออบเจกต์จากคลาส ส่วนด้านขวาเป็นการใช้รูปแบบ command การเขียนโปรแกรมในรูปแบบ command เป็น 1 ใน 12 รูปแบบการเขียนโปรแกรมในกลุ่มรูปแบบ behavioral ซึ่งจะใส่ใจกับการโต้ตอบกันระหว่างออบเจกต์เป็นหลัก

เราสามารถใช้รูปแบบการเขียนโปรแกรมแบบ command ร่วมกับ

  • การบรรจุคำสั่งลงใน queue และเรียกมาทำงานในภายหลัง : เพราะออบเจกต์ส่วนที่เป็นการเรียกใช้คำสั่งกับออบเจกต์ที่ทำตามคำสั่งแยกขาดจากกันทำให้เราสามารถเก็บออบเจกต์ที่เป็นการเรียกใช้คำสั่งไปเข้า queue เพื่อเรียกมาใช้งานตามลำดับ
  • การรองรับการทำ undo / redo : เพราะออบเจกต์ส่วนที่เป็นการเรียกใช้คำสั่งกับออบเจกต์ที่ทำตามคำสั่งแยกขาดจากกันทำให้เราสามารถเก็บลำดับของออบเจกต์ที่เป็นการเรียกใช้คำสั่งไว้เพื่ออ้างอิงลำดับการทำงานหากต้องการย้อนการขั้นตอนการทำคำสั่ง ซึ่งในกรณีนี้เราต้องสร้างเมธอดที่มีวิธีการทำงานที่ย้อนกลับผลลัพธ์จากทำคำสั่งต่างๆใน receiver ด้วย
  • การเก็บประวัติการทำงานของคำสั่ง : เพราะออบเจกต์ส่วนที่เป็นการเรียกใช้คำสั่งกับออบเจกต์ที่ทำตามคำสั่งแยกขาดจากกันทำให้เราสามารถเก็บลำดับของออบเจกต์ที่เป็นการเรียกใช้คำสั่งไว้เพื่ออ้างอิงได้
  • การเก็บลำดับการทำงานลงในดิสก์ : เพราะออบเจกต์ส่วนที่เป็นการเรียกใช้คำสั่งกับออบเจกต์ที่ทำตามคำสั่งแยกขาดจากกันทำให้เราสามารถเก็บลำดับของออบเจกต์ที่เป็นการเรียกใช้คำสั่งลงในไฟล์ได้
  • การนำคำสั่งหลายๆคำสั่งมาประกอบกันเป็นคำสั่งเดียวที่เรียกว่า macro : เพราะออบเจกต์ส่วนที่เป็นการเรียกใช้คำสั่งกับออบเจกต์ที่ทำตามคำสั่งแยกขาดจากกันทำให้เราสามารถเก็บลำดับการทำงานของออบเจกต์ที่เป็นชุดของคำสั่งได้

ตัวอย่างการใช้การเขียนโปรแกรมในรูปแบบ command กับแอพพลิเคชั่น

  • ใช้เพื่อควบคุมการดำเนินการเมื่อมีการกดปุ่มหรือเมนูในรูปแบบการติดต่อผู้ใช้งานแบบ GUI เช่น ในการใช้งานแพคเกจ Swing จะมีอินเตอร์เฟส Action กรอบของการเรียกใช้คำสั่ง (เทียบได้กับอินเตอร์เฟส command ในตัวอย่างด้านบน) ซึ่งนอกจากจะเป็นการปฏิบัติตามคำสั่งที่ต้องการแล้ว ยังสามารถเพิ่มเติมการใช้ ไอคอน(icon) คีย์ลัด(shortcut) ข้อความช่วยเหลือ(tooltip text) หรือความสามารถอื่นๆได้ด้วย
  • การใช้งานเครือข่าย (networking) เช่น ส่งอ็อบเจ็กต์การเรียกใช้คำสั่งทั้งหมดผ่านเครือข่ายเพื่อดำเนินการบนเครื่องอื่น: ตัวอย่างเช่น ส่งให้ผู้เล่นเกมส์ใช้ในการสั่งดำเนินการจากเครื่องของผู้เล่นเกมส์
  • ใช้เก็บลำดับการทำงานเพื่อให้สามารถย้อนคำสั่งที่ทำไปได้ เช่น การทำ undo ของการทำกิจกรรมต่างๆ หรือการทำ roll back ในการติดตั้งโปรแกรมหรือการปรับปรุงฐานข้อมูล เป็นต้น
  • ในการเขียนโปรแกรมแบบหลายเธรด (miltithread) เราสามารถการแยกส่วนของตัวเรียกใช้งานคำสั่ง (invoke) กับตัวที่ทำงาน (receiver) ให้ทำงานคนละเธรด โดยตัวเรียกใช้งานคำสั่งจะเอาคำสั่งเข้าคิวไว้และส่งให้ตัวที่ทำงานทำเมื่อตัวที่ทำงานเสร็จงานในแต่ละงาน

ตัวอย่างด้านล่างเป็นอีกหนึ่งตัวอย่างในการเขียนโปรแกรมด้วยรูปแบบ command

import java.util.Scanner;

class Client {

    public static void main(String[] args) {

        Controller controller = new Controller();
        TV tv = new TV();

        int[] channelList = new int[3];

        Scanner scanner = new Scanner(System.in);
        for (int i = 0; i < 3; i++) {
            channelList[i] = scanner.nextInt();
        }

        Command turnOnTV = new TurnOnCommand(tv);
        controller.setCommand(turnOnTV);
        controller.executeCommand();

        Command changeChannel;
        for (int i = 0; i < 3; i++) {
            Channel channel = new Channel(tv,channelList[i]);
            changeChannel = new ChangeChannelCommand(channel);
            controller.setCommand(changeChannel);
            controller.executeCommand();
        }

        Command turnOffTV = new TurnOffCommand(tv);
        controller.setCommand(turnOffTV);
        controller.executeCommand();
    }
}

class TV {

    Channel channel;

    void turnOn() {
        System.out.println("Turning on the TV");
        setChannel(new Channel(this, 0));
    }

    void turnOff() {
        System.out.println("Turning off the TV");
    }

    void setChannel(Channel channel) {
        this.channel = channel;
    }
}

class Channel {
    private TV tv;
    private int channelNumber;

    Channel(TV tv, int channelNumber) {
        this.tv = tv;
        this.channelNumber = channelNumber;
    }

    void changeChannel() {
        tv.setChannel(this);
        System.out.println("Channel was changed to " + channelNumber);
    }
}

interface Command {
    void execute();
}

class TurnOnCommand implements Command {
    private TV tv;

    TurnOnCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOn();
    }
}

class TurnOffCommand implements Command {
    private TV tv;

    TurnOffCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOff() ;
    }
}

class ChangeChannelCommand implements Command {

    private Channel channel;

    ChangeChannelCommand(Channel channel) {
        this.channel = channel;
    }

    @Override
    public void execute() {
        channel.changeChannel();
    }
}

class Controller {
    private Command command;
    void setCommand(Command command) {
        this.command = command;
    }
    void executeCommand() {
        command.execute();
    }
}