LoginSignup
1
1

【Gang of Four】Command

Last updated at Posted at 2019-03-18

#Command
目次
本パターンの主旨は、要求をオブジェクト化することで動的に要求を編集できることと、一連の処理を一つのオブジェクトに纏め共通のインターフェースを用意することで保守管理を簡単にすることを実現する、でしょうか。

よくこんなメソッドを見かけます。

Receiver.kt
    fun run(mode: ModeType) {
        when(mode) {
            ModeType.Begin -> {
                // 準備処理
                loadProperty()
                startDriver()
            }
            ModeType.Running -> {
                // メイン処理
                running()
            }
            ModeType.After -> {
                // 終了処理
                saveState()
                stopDriver()
            }
        }
    }

    private fun loadProperty(){}
    private fun startDriver(){}
    private fun running(){}
    private fun saveState(){}
    private fun stopDriver(){}

中断処理が必要になったため、saveState()のみ呼びだすモードがほしくなったのでモードと実装を追加します。

Receiver.kt
        ModeType.Interruption -> {
            // 中断処理
            saveState()
        }

また更に別のモードを…となってくるとReceiverクラスを延々と修正しなければならず、Modeもどんどん増えていきます。これを本パターンを適用することでReceiverクラスを修正することなく、柔軟な振る舞いをさせることができるようになります。

##目的
要求をオブジェクトとしてカプセル化することによって、異なる要求や、要求からなるキューやログにより、クライアントをパラメータ化する。また、取り消し可能なオペレーションをサポートする。

##構成要素
・Command Receiverクラスによって実行されるメソッドを定義する抽象クラス
・ConcreteCommand Commandクラスの具象クラス
・Client 使う人
・Invoker
・Receiver 命令の出し方を知っているクラス

##実装
###Receiver 命令の出し方を知っているクラス
インターフェースと

Receiver.kt
package command

interface Receiver {
    fun getName(): String
}

具象クラス

Car.kt
package command

class Car(private val name: String): Receiver {

    override fun getName(): String {
        return "レシーバは${name}オブジェクトです"
    }

    fun openDoor() {
        println("ドアを開ける")
    }

    fun engineStart() {
        println("エンジンスタート")
    }

    fun engineStop() {
        println("エンジンストップ")
    }

    fun lock() {
        println("ロックする")
    }

    fun unlock() {
        println("ロックを解除する")
    }

    fun pushAxelPedal() {
        println("アクセルを踏む")
    }

    fun pushBreakePedal() {
        println("ブレーキペダルを踏む")
    }
}

###Command Receiverクラスによって実行されるメソッドを定義する抽象クラス

Command.kt
package command

interface Command {
    fun execute()
}

###ConcreteCommand Commandクラスの具象クラス

SimpleCommand.kt
package command

class SimpleCommand(private val receiver: Receiver, private val method: String): Command {
    override fun execute() {
        receiver.javaClass.getDeclaredMethod(method).invoke(receiver)
    }
}
MacroCommand.kt
package command

import kotlin.collections.ArrayList

class MacroCommand: Command {
    private val commandList = ArrayList<Command>()

    override fun execute() {
        commandList.forEach {
            it.execute()
        }
    }

    fun addCommand(command: Command) {
        commandList.add(command)
    }

    fun removeCommand(command: Command) {
        commandList.remove(command)
    }
}

###Client
使う人
車のエンジンをかけるコマンドを作成します。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createStartCarCommand(receiver).execute()
    }

    private fun createStartCarCommand(receiver: Car): Command {
        val startCarCommand = MacroCommand()
        startCarCommand.addCommand(SimpleCommand(receiver, "unlock"))
        startCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        startCarCommand.addCommand(SimpleCommand(receiver, "engineStart"))
        return startCarCommand
    }
}

実行結果

[out-put]
ロックを解除する
ドアを開ける
エンジンスタート

車のエンジンをとめるコマンドが欲しくなりました。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createStopCarCommand(receiver).execute()
    }

    private fun createStopCarCommand(receiver: Car): Command {
        val stopCarCommand = MacroCommand()
        stopCarCommand.addCommand(SimpleCommand(receiver, "engineStop"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "lock"))
        return stopCarCommand
    }
}

実行結果

[out-put]
エンジンストップ
ドアを開ける
ロックする

車を走らせるコマンドを作成したくなりました。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createCarRunCommand(receiver).execute()
    }

    private fun createCarRunCommand(receiver: Car): Command {
        return SimpleCommand(receiver, "pushAxelPedal")
    }
}

実行結果

[out-put]
アクセルを踏む

車のエンジンをかけ、しばらく走ったあとエンジンをとめるコマンドが必要になりました。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createStartAndRunAndStopCarCommand(receiver).execute()
    }

    private fun createStartAndRunAndStopCarCommand(receiver: Car): Command {
        val startAndRunAndStopCarCommand = MacroCommand()
        startAndRunAndStopCarCommand.addCommand(createStartCarCommand(receiver))

        val runCommand = createCarRunCommand(receiver)
        startAndRunAndStopCarCommand.addCommand(runCommand)
        startAndRunAndStopCarCommand.addCommand(runCommand)
        startAndRunAndStopCarCommand.addCommand(runCommand)
        startAndRunAndStopCarCommand.addCommand(runCommand)

        startAndRunAndStopCarCommand.addCommand(createStopCarCommand(receiver))

        return startAndRunAndStopCarCommand
    }

    private fun createStartCarCommand(receiver: Car): Command {
        val startCarCommand = MacroCommand()
        startCarCommand.addCommand(SimpleCommand(receiver, "unlock"))
        startCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        startCarCommand.addCommand(SimpleCommand(receiver, "engineStart"))
        return startCarCommand
    }

    private fun createCarRunCommand(receiver: Car): Command {
        return SimpleCommand(receiver, "pushAxelPedal")
    }

    private fun createStopCarCommand(receiver: Car): Command {
        val stopCarCommand = MacroCommand()
        stopCarCommand.addCommand(SimpleCommand(receiver, "engineStop"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "lock"))
        return stopCarCommand
    }
}

###出力結果

[out-put]
ロックを解除する
ドアを開ける
エンジンスタート
アクセルを踏む
アクセルを踏む
アクセルを踏む
アクセルを踏む
エンジンストップ
ドアを開ける
ロックする

GoF本の適用可能性を読むと、このパターンの肝は要求をオブジェクト化することによって任意の要求を取り消しできること、所謂MacroCommandクラスのremoveCommandメソッドを呼び出すことらしいのですが、イマイチ必要になるシチュエーションが思い浮かびません。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1