【Rust】デザインパターン「Command」をRustで実装してみる

今回はタイトルの通り、デザインパターンの1つ、「Command」をRustで実装してみました。

デザインパターンオブジェクト指向言語に向けてのものでRustはオブジェクト指向言語ではないですが、それでもパターンを知っておくとコーディングするときの引き出しになって良いかなと思って挑戦してみました。

デザインパターンやCommandについては良い説明をされてるサイトがたくさんありますので、ここではRustで書いてみたコードを載せるだけにします。

//! デザインパターン「Command」のRustでの実装例

/// それぞれのコマンド(命令)のインタフェースを定義するトレイト
trait Command {
    /// コマンドを実行するメソッド
    fn execute(&self);
    /// コマンドを「元に戻す(反対の処理を行う)」メソッド
    fn undo(&self);
}

/// Addコマンドを定義する構造体
struct Add;
/// Addコマンドのメソッドを記述
impl Command for Add {
    /// Addを実行する
    /// ここではただ"Add"と出力するだけ
    fn execute(&self) {
        println!("Add")
    }
    /// Addのコマンド処理を元に戻す
    /// ここでは元に戻す=Subを行うこと("Sub"と出力する)にしている
    fn undo(&self) {
        println!("Sub")
    }
}

/// Mulコマンドを定義する構造体(とメソッド)
struct Mul;
/// Mulコマンドのメソッドを記述
impl Command for Mul {
    /// Mulを実行する
    /// ここではただ"Mul"と出力するだけ
    fn execute(&self) {
        println!("Mul")
    }
    /// Mulのコマンド処理を元に戻す
    /// ここでは元に戻す=Divを行うこと("Div"と出力する)にしている
    fn undo(&self) {
        println!("Div")
    }
}

/// コマンドを呼ぶ人とコマンド自体を仲介したり、コマンド履歴の保持をする構造体
///
/// ちなみにInvokerとは起動者の意味
struct Invoker {
    /// コマンド履歴を保持するベクタ
    history: Vec<Box<dyn Command>>,
}

impl Invoker {
    /// オブジェクトを作成する
    fn new() -> Self {
        Self { history: vec![] }
    }

    /// 引数に与えられたコマンドを実施し、履歴に格納する
    fn execute(&mut self, command: Box<dyn Command>) {
        command.execute();
        self.history.push(command);
    }

    /// 直前に行った処理を元に戻す
    ///もし直前に行った処理がない場合はその旨を出力する
    fn undo(&mut self) {
        if self.history.len() > 0 {
            self.history.pop().unwrap().undo();
        } else {
            println!("stacked command is zero");
        }
    }
}

/// エントリーポイント
/// いくつかのコマンドを実行し、元に戻す処理を行っている
fn main() {
    let mut invoker = Invoker::new();

    invoker.execute(Box::new(Add));
    invoker.execute(Box::new(Mul));
    invoker.execute(Box::new(Add));
    invoker.undo();
    invoker.undo();
    invoker.undo();
    invoker.undo();
}