תבנית Command

בהנדסת תוכנה, תבנית Command, כפי שנובע משמה, היא תבנית שמייצגת פקודות. היא נועדה לעטוף (או להסתיר) את כל המידע הרלוונטי להרצת מתודה מסוימת (המופע ממנו קוראים לה, שמה, והפרמטרים שנשלחים לה) בתוך אובייקט יחיד. התבנית שימושית במימוש של תפריטים, ביצירת Progress Bar, בתבנית Thread Pools ועוד.

מבנה

מימוש
במימוש התבנית ארבעה מרכיבים עיקריים:
 Command - ממשק המכיל בדרך כלל מתודה אחת - execute.
 ConcreteCommand - מחלקה אשר מממשת את הממשק. בדרך כלל ניצור הרבה מחלקות כאלה - אחת לכל פקודה.
 Invoker - תפקידה לבצע את הקריאה למתודה execute של ה-ConcreteCommand הנבחר. לעיתים תכיל טבלה של כל המימושים הקיימים לCommand ותקבל רק מספר או שם שמייצגים את המימוש הרצוי (לצורך יעילות).
 Receiver - המחלקה עליה בעצם מבוצעת הפקודה.

דוגמאות מימוש

Java

/*the Command interface*/

public interface Command{
    void execute();
}

/*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();
    }

}

/*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 for turning on the light - ConcreteCommand #1*/

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 - ConcreteCommand #2*/

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 );

       Switch s=new Switch(switchUp,switchDown);

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

   }
}

C#
using System;

//the Command Interface
public interface ICommand
{
	void Execute();
}

//The ConcreteCommands
public class DVDPlayCommand : ICommand
{
	public DVDPlayCommand(){}
	public void Execute()
	{
		Console.WriteLine("DVD Started.");
	}
}

public class DVDStopCommand : ICommand
{
	public DVDStopCommand(){}
	public void Execute()
	{
		Console.WriteLine("DVD Stopped.");
	}
}

public class VCRPlayCommand : ICommand
{
	public VCRPlayCommand(){}
	public void Execute()
	{
		Console.WriteLine("VCR Started.");
	}
}

public class VCRStopCommand : ICommand
{
	public VCRStopCommand(){}
	public void Execute()
	{
		Console.WriteLine("VCR Stopped.");
	}
}

//The Invoker
public class Remote
{
	public Remote(){}
	public void Invoke(ICommand cmd )
	{
		Console.WriteLine("Invoking.......");
		cmd.Execute();
	}
}

//The Client
public class Client
{
	public Client(){}
	public static int Main(String[] args)
	{
		//Instantiate the invoker object
		Remote remote = new Remote();
		//Instantiate DVD related commands and pass them to invoker object
		DVDPlayCommand dvdPlayCommand = new DVDPlayCommand();
		remote.Invoke(dvdPlayCommand);
		DVDStopCommand dvdStopCommand = new DVDStopCommand();
		remote.Invoke(dvdStopCommand);
		//Instantiate VCR related commands and pass them to invoker object
		VCRPlayCommand vcrPlayCommand = new VCRPlayCommand();
		remote.Invoke(vcrPlayCommand);
		VCRStopCommand vcrStopCommand = new VCRStopCommand();
		remote.Invoke(vcrStopCommand);
		return 0;
	}
}

שימושים ידועים
 שחזור (undo) - אם כל הפקודות של מערכת מסוימת יוחזקו כאובייקטים של Command אז התוכנית יכולה להחזיק מחסנית של כל הפקודות, ובביצוע undo להריץ את המתודה undo של הפקודה שבראש המחסנית (אותה שיטה יכולה להיות שימושית אפילו יותר אם מממשים טרנזקציות).
 תפריטים ו-GUI - במימוש של תפריט, כל פעולה ניתנת למימוש על ידי command, מה שמקל על המימוש והסדר בו ניגשים לדברים.
 Thread pools - התור של הפעולות שיש לבצע יכול להיות ממומש על ידי אובייקטים של Commands, והתהליכים פשוט יבצעו execute.

מקורות
 http://www.c-sharpcorner.com/UploadFile/cupadhyay/CommandPatternsInCS11142005021734AM/CommandPatternsInCS.aspx
 https://web.archive.org/web/20060318215341/http://www.mh2000.co.il/articles/update67-CommandPattern.pdf

קישורים חיצוניים

Command