PHPでStrategyパターン

使うメリット

  • メール、SMS、プッシュ通知、今後増える可能性がある通知手段を、他実装に影響せず追加が行える

2016/12/11 に追記し、本記事最下部に変更したソースあり

<?php

// 抽象クラス
abstract class MessageStrategy {
    public abstract function send();
}

// コンテキストクラス
class MessageContext {
    private $strategy;

    public function __construct(MessageStrategy $message) {
       $this->strategy = $message;
    }

    public function send() {
        $this->strategy->send();
    }
}

// 具象クラス
class ConcreteMailMessage extends MessageStrategy {
    public function __construct() {
        // 前処理
    }

    public function send() {
        echo 'mail'.PHP_EOL;
    }
}
class ConcreteSMSMessage extends MessageStrategy {
    public function __construct() {
        // 前処理
    }

    public function send() {
        echo 'sms'.PHP_EOL;;
    }
}
class ConcretePushNotificationMessage extends MessageStrategy {
    public function __construct() {
        // 前処理
    }

    public function send() {
        echo 'push'.PHP_EOL;;
    }
}

$strategy = new ConcreteMailMessage();
$contextMessage = new MessageContext($strategy);
$contextMessage->send();

これから調べる事

  • 送信時にユーザクラスからmail/tel/token などを取得したいが、どのクラスで行うか
  • Userクラスに依存するので、Strategyパターンを使うのではなく、違うパターンが正解かもしれない

追記 2016/12/11

  • Uesrクラスは具象クラスにコンストラクタインジェクションする事にした
    • コンストラクタで注入する事で、各通知処理の前処理が行えると考えた
    • 仮にアプリユーザというクラス(mail / name を持たずtokenだけ持つクラスを利用する事があっても、具象クラスのみ影響があるため、他クラスは影響を受けないと考えた
    • テストコードを追加したい
<?php

// 送信対象のUserクラス
class User {
    private $name;
    private $mail;
    private $tel;

    // 仮に利用するpropertyを定義
    public function __construct($name, $mail, $tel) {
        $this->name = $name;
        $this->mail = $mail;
        $this->tel  = $tel;
    }

    // 仮に名前だけ出力するためにgetter設置
    public function getName() {
        return $this->name;
    }

}

// 抽象クラス
abstract class MessageStrategy {
    protected $user;
    public abstract function send();
    public abstract function __construct(User $user);
}

// コンテキストクラス
class MessageContext {
    private $strategy;

    public function __construct(MessageStrategy $message) {
        $this->strategy = $message;

    }

    public function send() {
        $this->strategy->send();
    }
}

// 具象クラス(Mail)
class ConcreteMailMessage extends MessageStrategy {
    public function __construct(User $user) {
        $this->user = $user;
        // mailに関する前処理
        // ...
    }

    public function send() {
        echo 'mail->to('. $this->user->getName(). ')'. PHP_EOL;
    }
}

// 具象クラス(SMS)
class ConcreteSMSMessage extends MessageStrategy {
    public function __construct(User $user) {
        $this->user = $user;
        // smsに関する前処理
        // ...
    }

    public function send() {
        echo 'sms->to('. $this->user->getName(). ')'. PHP_EOL;
    }
}

// 具象クラス(PushNotification)
class ConcretePushNotificationMessage extends MessageStrategy {
    public function __construct(User $user) {
        $this->user = $user;
        // Push通知に関する前処理
        // ...
    }

    public function send() {
        echo 'push->to('.$this->user->getName(). ')'. PHP_EOL;
    }
}

// 送信対象のテストUserインスタンス
$user = new User('Pさん', 'test@example.com', '090xxxxyyyy');

// Mail送信
$strategy = new ConcreteMailMessage($user);

// $strategyにどのクラスを代入するかでMail / SMS / PushNotification とsend()の振る舞いを変える
// SMS送信
// $strategy = new ConcreteSMSMessage($user);

// Push通知
// $strategy = new ConcretePushNotificationMessage($user);

// Contextクラスにstrategyクラスを注入
$contextMessage = new MessageContext($strategy);

// 送信処理 注入されたオブジェクトのsend()メソッドをcall
$contextMessage->send();

// mail->to(Pさん) と出力される