どのようなときにコマンドパターンを活用するか
コマンドパターンの解説
コマンドパターンは、コマンド(命令)実行のタイミングをずらしたいときや(非同期処理)、コマンドを単純な文字列からなるパラメータ(エンティティ)として管理したいときに活用できる。 ペイントツールや図形ツールなど、取り消し機能と多数コマンドからなるシステムを開発するときに取り入れるとよい。コマンドパターンのクラス図
どのようなときにストラテジーパターン(Strategy パターン)を適用するか?
メソッドの振る舞い(ロジック部分)を状況によって変更切替ができるようにしたいとき。解説
ストラテジーパターンを適用すると環境やステータスによってメソッドの振る舞いを切り替えることができる。IF文との比較
ストラテジーパターンを乱用してはならない。 振る舞いの変更はif文でもできるので普段はこちらを使うべき。ストラテジーパターンのクラス図
ソースコード(PHP)
<?php
class Animal{
private $barkStrategy;
public function __construct($mode='neko'){
// 下記のストラテジー変更は一例に過ぎない。ストラテジーを変更する方法はいろいろある。
$barkStrategy = new CatStrategy();
if($mode == 'inu'){
$this->setStrategy(new DogStrategy());
}else if($mode=='buta'){
$this->setStrategy(new PigStrategy());
}else{
$this->setStrategy(new CatStrategy());
}
}
public function setStrategy(IBarkStrategy $barkStrategy){
$this->barkStrategy = $barkStrategy;
}
public function bark(){
$this->barkStrategy->bark();
}
}
interface IBarkStrategy{
public function bark();
}
// ストラテジーインターフェース
class CatStrategy implements IBarkStrategy{
public function bark(){
echo 'ニャオーン';
}
}
class DogStrategy implements IBarkStrategy{
public function bark(){
echo 'ワン ワン';
}
}
class PigStrategy implements IBarkStrategy{
public function bark(){
echo 'キーキー';
}
}
// 検証
$animal = new Animal('inu');
$animal->bark(); // → ワン ワン
echo '<br>';
$animal->setStrategy(new PigStrategy());
$animal->bark(); // → キーキー
echo '<br>';
$animal->setStrategy(new CatStrategy());
$animal->bark(); // → ニャオーン
echo '<br>';
?>
どんなときにオブザーバーパターンを活用するか?
サブジェクト(主体クラス)からの更新通知を受け取って処理を行いたいとき。解説
オブザーバーには「観察者」という意味がある。 その名前の通り、オブザーバークラスはサブジェクトクラス(主体クラス)からの更新通知を観察し、通知を確認したら何らかの処理を行う。オブザーバーパターンのクラス図
オブザーバーパターンのサンプルソースコード
interface IObserver{
public function update($data);
}
class LogObserver implements IObserver{
public function update($data){
echo 'ログ出力を行いました。(ダミー)'.$data['date1'].'<br>';
}
}
class MailObserver implements IObserver{
public function update($data){
echo 'メール送信を行いました。(ダミー)'.$data['date1'].'<br>';
}
}
class TwitterObserver implements IObserver{
public function update($data){
echo 'Twitterへ投稿しました。(ダミー)'.$data['date1'].'<br>';
}
}
class SubjectBase{
public $observers = array();
public function addObserver(IObserver $observer){
$this->observers[] = $observer;
}
public function notifyObservers($data){
for($i=0; $i<count($this->observers); $i++){
$observer = $this->observers[$i];
$observer->update($data);
}
}
}
class AnimalSubject extends SubjectBase{
public function __construct(){
$this->addObserver(new LogObserver());
$this->addObserver(new MailObserver());
$this->addObserver(new TwitterObserver());
}
public function action(){
$data = array('date1' => date('Y-m-d H:i:s'));
$this->notifyObservers($data);
}
}
// 検証
$animal = new AnimalSubject();
$animal->action();
出力ログ出力を行いました。(ダミー)2018-06-05 11:02:45 メール送信を行いました。(ダミー)2018-06-05 11:02:45 Twitterへ投稿しました。(ダミー)2018-06-05 11:02:45
ステートパターンの解説
ステートパターンの基本的な仕組みは「状態」と「アクション」の総当たり組み合わせである。 MECEという思考法と基本的に同じ。ステートパターンのメリット
ステートパターンを適用すると「状態」や「アクション」に仕様変更または追加があっても対応しやすくなるメリットがある。 つまり保守性の高いコードになる。if文との比較
「状態」は、ステートパターンを使わずともif文だけで実現可能。ステートパターンの弱点
ステートパターンは一度に1つの状態にしか適用できない。 つまり、一度に2つ以上の状態にすることはできない。ステートパターンのクラス図
状態とアクションの総当たり組み合わせ(MECE)
状態 | アクション | 処理 |
---|---|---|
予約状態(ReservState) | 予約する(reservation) | 予約済みです。 |
予約状態 | チェックインする(checkin) | チェックインし、滞在状態になりました。 |
予約状態 | チェックアウトする(checkout) | 予約状態でチェックアウトはできません。 |
滞在状態(StayState) | 予約する | 別件で予約状態にします。 |
滞在状態 | チェックインする | チェックイン済みです。 |
滞在状態 | チェックアウトする | チェックアウトして、未予約状態になりました。 |
未予約状態(UnreservState) | 予約する | 予約して、予約状態になりました。 |
未予約状態 | チェックインする | 空き室があれば滞在状態になります。空き室がなければ未予約状態のままです。 |
未予約状態 | チェックアウトする | チェックインしていないのでチェックアウトできません。 |
ステートパターンのサンプルソースコード(PHP)
interface IState{
public function reservation();
public function checkin();
public function checkout();
}
class ReservState implements IState{
private $subject;
public function __construct(Hotel $subject){
$this->subject = $subject;
}
public function reservation(){
echo '予約済みです。<br>';
}
public function checkin(){
echo 'チェックインし、滞在状態になりました。<br>';
$this->subject->changeState(new StayState($this->subject));
}
public function checkout(){
echo '予約状態でチェックアウトはできません。<br>';
}
}
class StayState implements IState{
private $subject;
public function __construct(Hotel $subject){
$this->subject = $subject;
}
public function reservation(){
echo '別件で予約状態にします。<br>';
}
public function checkin(){
echo 'チェックイン済みです。<br>';
}
public function checkout(){
echo 'チェックアウトして、未予約状態になりました。<br>';
$this->subject->changeState(new UnreservState($this->subject));
}
}
class UnreservState implements IState{
private $subject;
public function __construct(Hotel $subject){
$this->subject = $subject;
}
public function reservation(){
echo '予約して、予約状態になりました。<br>';
$this->subject->changeState(new ReservState($this->subject));
}
public function checkin(){
echo '空き室があれば滞在状態になります。空き室がなければ未予約状態のままです。<br>';
}
public function checkout(){
echo 'チェックインしていないのでチェックアウトできません。<br>';
}
}
class Hotel{
private $state; // <IState>
public function __construct(){
$this->state = new UnreservState($this); // 未予約状態
}
/**
* 状態を変更する
* @param IState $state 状態クラスのインスタンス
*/
public function changeState(IState $state){
$this->state = $state;
}
public function reserveAction(){
$this->state->reservation();
}
public function checkinAction(){
$this->state->checkin();
}
public function checkoutAction(){
$this->state->checkout();
}
}
$hotel = new Hotel();
$hotel->reserveAction();
$hotel->checkinAction();
$hotel->checkinAction();
$hotel->checkoutAction();
出力
予約して、予約状態になりました。 チェックインし、滞在状態になりました。 チェックイン済みです。 チェックアウトして、未予約状態になりました。
どんなときにデコレーターパターンを使うか?
デコレーターパターンの解説
オブジェクト指向には、「修正ではなく拡張をする」という原則がある。 既存のソースコードは時間をかけてテストをした品質が高いものである。 そのためできるだけ既存のソースコードに手を入れず、拡張するようにしたほうが良い。 デコレーターパターンは、この原則を当てはめるときに役に立つ。デコレーターパターンのクラス図
デコレーターパターンのサンプルソースコード
class Pet{
public $name = 'none';
public function getName(){
return $this->name;
}
public function bark(){
return '吠える';
}
public function price(){
return 0;
}
}
class Cat extends Pet{
public function __construct(){
$this->name = 'ネコ';
}
public function bark(){
return 'ニャゴー';
}
public function price(){
return 10000;
}
}
class Dog extends Pet{
public function __construct(){
$this->name = '犬';
}
public function bark(){
return 'ワンワン';
}
public function price(){
return 10;
}
}
// デコレータークラス
class Decorator extends Pet{
protected $pet;
public function __construct(Pet $pet){
$this->pet = $pet;
}
public function getName(){
return $this->pet->getName();
}
public function bark(){
return $this->pet->bark();
}
public function price(){
return $this->pet->price();
}
}
class Big extends Decorator{
public function getName(){
return '大きい' . $this->pet->getName();
}
public function price(){
return $this->pet->price() + 500;
}
}
class Small extends Decorator{
public function getName(){
return '小さい' . $this->pet->getName();
}
public function price(){
return $this->pet->price() + 200;
}
}
class Black extends Decorator{
public function getName(){
return '黒い' . $this->pet->getName();
}
public function price(){
return $this->pet->price() + 50;
}
}
class Bowlingual extends Decorator{
public function bark(){
return $this->pet->bark() . '(今日の天気はいかがですか)';
}
}
// 検証
$cat = new Cat();
output($cat);
$bigCat = new Big($cat);
output($bigCat);
$smallCat = new Small($cat);
output($smallCat);
$blackCat = new Bowlingual($smallCat);
output($blackCat);
$blackCat = new Black($smallCat);
$dog = new Dog();
$dog = new Black($dog);
$dog = new Big($dog);
output($dog);
function output(Pet $pet){
echo $pet->getName() . ' → ' . $pet->bark() . ' → ' . $pet->price() . '円<br>';
}
出力
予約して、予約状態になりました。 チェックインし、滞在状態になりました。 チェックイン済みです。 チェックアウトして、未予約状態になりました。
シンプルファクトリーのクラス図
シンプルファクトリーのサンプルソースコード(PHP)
class Park{
public function action(){
// ファクトリー
$factory = new SimpleFactory();
$animal = $factory->createAnimal('cat');
$animal->bark();
$animal = $factory->createAnimal('dog');
$animal->bark();
}
}
class SimpleFactory{
public function createAnimal($code){
$animal = null;
if($code=='cat'){
$animal = new Cat();
}else if($code=='dog'){
$animal = new dog();
}
return $animal;
}
}
interface IAnimal{
public function bark();
}
class Cat implements IAnimal{
public function bark(){
echo 'ゴロニャゴ<br>';
}
}
class Dog implements IAnimal{
public function bark(){
echo 'ワオーム<br>';
}
}
// 検証
$park = new Park();
$park->action();
出力
ゴロニャゴ ワオーム
どんなときにFactory Methodを用いるか?
主体クラスが複数のサブクラスに分かれており、そしてサブクラスごとにファクトリーで生成するProductクラス(製品クラス)が異なる場合に利用する。Factory MethodとAbstract Factoryの比較
Factory Methodは継承を使っているのに対し、Abstract Factoryはコンポジションを使っている。ファクトリーメソッドのクラス図
ファクトリーメソッドのサンプルソースコード(PHP)
abstract class Park{
public function action($code){
$animal = $this->createAnimal($code);
$animal->bark();
}
abstract public function createAnimal($code);
}
class UsPark extends Park{
public function createAnimal($code){
$animal = null;
if($code == 'cat'){
$animal = new AmericanShortHair();
}else if($code='dog'){
$animal = new AmericanTerrier();
}
return $animal;
}
}
class JapanPark extends Park{
public function createAnimal($code){
$animal = null;
if($code == 'cat'){
$animal = new MikeNeko();
}else if($code='dog'){
$animal = new SibaInu();
}
return $animal;
}
}
interface IAnimal{
public function bark();
}
class MikeNeko implements IAnimal{
public function bark(){
echo 'ミケー<br>';
}
}
class SibaInu implements IAnimal{
public function bark(){
echo 'ワン ワン<br>';
}
}
class AmericanShortHair implements IAnimal{
public function bark(){
echo 'meow<br>';
}
}
class AmericanTerrier implements IAnimal{
public function bark(){
echo 'bow bow<br>';
}
}
// 検証
$usPark = new UsPark();
$usPark->action('cat');
$usPark->action('dog');
$jpPark = new JapanPark();
$jpPark->action('cat');
$jpPark->action('dog');
meow bow bow ミケー ワン ワン
どんなときにAbstractファクトリーを使うか?
ファクトリーで生成するProductクラス(製品クラス)が多数存在するとき、 大量のProductクラスをグループとして仕分けしたほうが良い。アブストラクトファクトリーのクラス図
アブストラクトファクトリーのサンプルコード
class ParkA{
public $factory;
public function setFactory(IFactory $factory){
$this->factory = $factory;
}
public function action(){
$cat = $this->factory->createCat();
$cat->crow_talk();
$dog = $this->factory->createDog();
$dog->bark();
}
}
interface IFactory{
public function createCat();
public function createDog();
}
class AmericanFactory implements IFactory{
public function createCat(){
return new AmericanShortHair();
}
public function createDog(){
return new AmericanTerrier();
}
}
class JapanFactory implements IFactory{
public function createCat(){
return new MikeNeko();
}
public function createDog(){
return new SibaInu();
}
}
interface ICat{
public function crow_talk();
}
interface IDog{
public function bark();
}
class MikeNeko implements ICat{
public function crow_talk(){
echo '三毛猫→ ニャー ニャー<br>';
}
}
class SibaInu implements IDog{
public function bark(){
echo '柴犬→ ワン ワン<br>';
}
}
class AmericanShortHair implements ICat{
public function crow_talk(){
echo 'アメショ→ mew mew<br>';
}
}
class AmericanTerrier implements IDog{
public function bark(){
echo 'テリア→ bow bow<br>';
}
}
// 検証
$park = new ParkA();
$park->setFactory(new AmericanFactory());
$park->action();
$park->setFactory(new JapanFactory());
$park->action();
シングルトンパターンクラスの特徴
コンストラクタをPrivateにすることにより new を禁止する。インスタンス取得はgetInstance()を使う。シングルトンパターンのクラス図
シングルトンパターンのサンプルソースコード(PHP)
class Singleton{
private static $instance; // 自分自身のインスタンス
private $test=0;
private function __construct(){
}
public static function getInstance(){
if(self::$instance == null){
self::$instance = new Singleton();
}
return self::$instance;
}
public function testMethod(){
$this->test ++;
echo $this->test.'<br>';
}
}
$singlton1 = Singleton::getInstance();
$singlton1->testMethod();
$singlton2 = Singleton::getInstance();
$singlton2->testMethod();
$singlton3 = Singleton::getInstance();
$singlton3->testMethod();
デコレーターパターンとの違い
アダプターパターンは既存のクラスを変更せずラップするという点でデコレーターパターンをよく似ている。 しかし役割が異なる。デコレーターパターンは仕様拡張に用いられるが、アダプターパターンは仕様を適合させるのに用いられる。アダプターパターンのクラス図
アダプターパターンのサンプルソースコード(PHP)
interface ICat{
public function crow();
}
class KuroNeko implements ICat{
public function crow(){
echo 'ニャオー<br>';
}
}
class BigCat implements ICat{
public function crow(){
echo 'マーオ<br>';
}
}
class Lion{
public function bark(){
echo 'ガオオォ<br>';
}
}
class LionAdapter implements ICat{
private $lion;
public function __construct(){
$this->lion = new Lion();
}
public function crow(){
echo $this->lion->bark();
}
}
// 検証
$cats = [];
$cats[] = new KuroNeko();
$cats[] = new BigCat();
$cats[] = new LionAdapter();
foreach($cats as $cat){
$cat->crow();
}
出力
ニャオー マーオ ガオオォ