TemplateMethodパターン

どうも、今月ブログ更新をサボり気味で後悔してます。
「ブログ続けてるのすごい」って言われて嬉しかったので、
更新ちゃんと頑張ります。
遡ってみると2017年1月からブログを始めて早2年経ってました。
累計113記事でした。

\目標、1000記事/

今回は、TemplateMethodについてです。

 

TemplateMethodとは?

メソッドにおけるアルゴリズムの骨組みを定義し、
いくつかの手順をサブクラスに先送りします。

→ アルゴリズムとなる一連の手順(メソッド)を
定義するメソッド(テンプレート)があり、
その一連の手順の中のメソッドの1つ以上を
抽象メソッドとして定義し、
サブクラスがそれを実装する

 

クラス図

テンプレートメソッドのクラス図

 

コード

abstract class AbstractClass
{
    public function templateMethod(): void
    {
        $this->primitiveOperation1();
        $this->primitiveOperation2();
    }

    //それぞれ異なる手順→サブクラスにまかせる
    abstract public function primitiveOperation1();

    public function primitiveOperation2(): void
    {
        //共通の手順
    }
}
class ConcreteClass extends AbstractClass
{
    public function primitiveOperation1(): void
    {
        // TODO: Implement primitiveOperation1() method.
    }
}

 

いざ、実装!

Head Firstデザインパターンの具体例では、
コーヒーと紅茶の作り方がでてきました。

レシピ

▶︎コーヒーの作り方
1.お湯を沸かす
2.フィルターをセットして淹れる
3.コップにコーヒーを注ぐ
4.ミルクや砂糖を加える

▶︎紅茶の作り方
1.お湯を沸かす
2.ティパックをセットしてお湯をいれる
3.レモンをいれる
4.コップに注ぐ

 

ざっくりみると、コーヒーと紅茶の作り方は似ているので、
1~4の一連の手順をテンプレートメソッドとしてスーパークラスに定義し、
「1.お湯を沸かす」の部分は共通化できるので、スーパークラスで実装します。
コーヒーと紅茶で異なる部分はサブクラスで実装します。

実際のコードはこちら↓
[kin29/HeadFirstDezaPata]

テンプレートメソッド適用前と後のbefore-after

 

クラス図

カフェインでのテンプレートメソッド

 

コード

abstract class CaffeineBeverage
{
    public function prepareRecipe(): void
    {
        $this->boilWater();
        $this->brew();
        $this->pourInCup();
        $this->addCondiments();
    }

    abstract public function brew(): void;

    abstract public function addCondiments(): void;

    public function boilWater(): void
    {
        echo "お湯を沸かします\n";
    }

    public function pourInCup(): void
    {
        echo "カップに注ぎます\n";
    }
}

 

class Coffee extends CaffeineBeverage
{
    public function __toString(): string
    {
        return "--- コーヒーのレシピ ---\n";
    }

    public function brew(): void
    {
        echo "フィルターでコーヒをドリップします\n";
    }

    public function addCondiments(): void
    {
        echo "砂糖とミルクを追加します\n";
    }
}

class Tea extends CaffeineBeverage
{
    public function __toString(): string
    {
        return "--- 紅茶のレシピ ---\n";
    }

    public function brew(): void
    {
        echo "紅茶を浸します\n";
    }

    public function addCondiments(): void
    {
        echo "レモンを追加します\n";
    }
}
$ php src/TemplateMethod/main.php
--- コーヒーのレシピ ---
お湯を沸かします
フィルターでコーヒをドリップします
カップに注ぎます
砂糖とミルクを追加します
--- 紅茶のレシピ ---
お湯を沸かします
紅茶を浸します
カップに注ぎます
レモンを追加します

メリット

・共通メソッドの変更が必要となった場合、
スーパクラスの変更のみでいい。

・スーパクラスで骨組みを定義しているので、
新しいカフェインクラスの追加が必要になった場合(例えばココアとか?)、
実装がしやすい

 

 

令和もがんばろーっっと

Facadeパターン

 

どうも!四年ぶりくらいに風邪ひいて病院いきました。
病院の看護婦さんが母のように優しくてホッとしました。

 

\今回はFacadeパターンです!/

 

前回の最小知識の原則~PrincipleOfLeastKnowledge

でも出しましたが、Facadeパターンについてまとめます。

 

Facadeパターンとは…

サブシステムの一連のインターフェースに対する、
統合されたインターフェースを提供する。
ファサードは、サブシステムをより使いやすくする
高水準インターフェースを定義する。

 

クラス図

・Facade   …Subsystemをより使いやすく統合したインターフェース

・Client     …ファサードのおかげで作業が簡単になった幸福なクライアン

・Subsystem   …より複雑なサブシステムたち

 

具体的な例

コードはこちら!

 

いろんな電気機器(アンプ/DVDプレーヤー/ポップコーンマシーン)の一連オペレーションを統括したインターフェースをもつ、ホームシアター[Facade]を実装します。

 

・HomeTheaterFacade.php[Facade]
…Subsystemの中で、ホームシアターに必要な
一連のオペレーションを統一したインターフェース

・main.php[Client]
…ホームシアターを使うクライアント

・Subsystem/[Subsystem]
…アンプやDVDプレーヤ、ポップコーンマシーン、
スクリーン、ライトなどの電気機器クラス

 

映画を見るための電気機器の一連のオペレーションを
Facadeパターンで作って、実行した例です。

main.php

$homeTheater = new HomeTheaterFacade(
    $amp,
    $tuner,
    $dvd,
    $cd,
    $projector,
    $screen,
    $light,
    $pop
);
$homeTheater->watchMovie('アナ雪');
$ php src/Facade/main.php
▶︎ 映画を見る準備をします!
ボップコーンマシーンをONにします
ボップコーンを作ります
シアターライトを10にします
スクリーンを下げてセットします
プロジェクターをONにします
プロジェクターをワイドスクリーンモードにします
アンプをONにします
アンプにDVDプレーヤをセットします
アンプにサラウンドサウンドをセットします
アンプにボリュームを5にします
DVDプレーヤーをONにします
DVD「アナ雪」をスタートします

 

Facadeにいろいろ統括系は任せてるので…

Subsystem側は自分の役目に専念できる。
Client側に使いやすいインターフェースをFacadeが提供できるので、
Clientも使いやすい!

 

依存性の高いコードになってしまうこともある!

最小知識の原則~PrincipleOfLeastKnowledgeにも書いたように、
複数のオブジェクトのメソッドを呼び出すことが多くなるので、
ある程度の決められた範囲内のメソッドのみを呼び出さないと、
依存性の高いコードになってしまいます。

なので、以下に気をつけて実装するとマルっ!
1. そのオブジェクト自身のメソッド
2. メソッドの引数として渡されたオブジェクトのメソッド
3. メソッドが作成またはインスタンス化したオブジェクトのメソッド
4. そのオブジェクトのコンポーネント(インスタンス変数で参照されるオブジェクト)[HAS-A関係]のメソッド

 

最小知識の原則~PrincipleOfLeastKnowledge~

「最小知識の原則」についての説明は、
Facadeパターンの章で出てきました。

Facadeパターンは、
サブシステムの一連のインターフェースに対する
統合されたインターフェースを提供するものです。

したがって、Facadeパターンは
複数のオブジェクトのメソッドを呼び出すことが想定されます。
しかし、ある程度の決められた範囲内のメソッドのみを呼び出さないと、
依存性の高いコードになってしまいます。
それを防ぐための原則が
「最小知識の原則」となります。

 

最小知識の原則

「直接の友達とだけ話すこと」
オブジェクト間のやりとりを最小限にするべき。

→多数のクラスが互いに結合し、
システムのある部分に対する変更が他部分に
連鎖してしまうような設計を回避する。

※デメテルの法則 (Law of Demeter, LoD) ともいう。

 

 

以下に属するメソッドだけを呼び出すべき

1. そのオブジェクト自身のメソッド
2. メソッドの引数として渡されたオブジェクトのメソッド
3. メソッドが作成またはインスタンス化したオブジェクトのメソッド
4. そのオブジェクトのコンポーネント(インスタンス変数で参照されるオブジェクト)[HAS-A関係]のメソッド

 

<?php

class Car
{
    //4.このクラスのコンポーネント(HAS-A)の
    //メソッドである、$engine.start()はOK!
    public $engin;

    public function __construct(){}

    //2.メソッドの引数として渡されたオブジェクト$keyの
    //メソッドである$key.turns()はOK!
    public function start(Key $key): void
    {
             //3.メソッドが作成したオブジェクト$doorsの
        //メソッドである$door.lock()はOK
        $doors = new Doors();  
        
        $authorized = $key.turns();
        
        if($authorized){
            $engine.start();
             
               //1.オブジェクト自身のメソッド
                    //である、updateDashboardDisplay()はOK!
            this->updateDashboardDisplay(); 
       
            $door.lock();
        }

    }

    public function updateDashboardDisplay()
    {
        //
    }

}

デメリット

この原則を意識しすぎると、ラッパークラスの記述が多くなり、
複雑になってしまうこともある。

なので、適当に活用する!



Adapterパターン

 

今年から、花粉症になったみたいです・・・。
鼻がムズムズして頭も痛くなるし大変ですね、なめてました。

 

 

今回は、
Adapterパターン
について書きます!

 

Adapterパターン

クラスのインターフェースを、
クライントの期待する別のインターフェースに変換する。

アダプタは互換性がないインターフェースのために、
そのままでは連携ができないクラス(Adaptee)を
(Clientと)連携できるようにさせます。

ラップするインターフェースを変換します。

 

 

▶︎ Adapter
Targetインターフェースを実装する。

 

▶︎ Target <<interface>>
ClientはこのTargetインターフェースを使っている感覚。
実際には、Adapterを通して、Adapteeを使っているが、
Adapteeを使っている認識は特にない。

 

▶︎ Adaptee
全てのリクエストはAdapeeに委譲される。
Adapteeの任意のサブクラスにもAdapterは適用できる
(コンポジションのため)

 

▶︎ Client
①Targetインターフェースを使って、
Adapterのメソッドを呼び出し、
Adapterにリクエストをする。
②AdapterがAdapteeインターフェースを使って
Adapteeのメソッドに変換して呼び出す
③Clientはリクエスト結果を受け取るが、
Targetインターフェースに沿っただけなので
変換をしてくれたAdapterの存在は知らない。

 

PHPで書いたみたサンプルコード

 

オブジェクトアダプタ/クラスアダプタ

実は、Adapterパターンには、
– オブジェクトアダプタ
– クラスアダプタ
の2つがある。

 

オブジェクトアダプタ

コンポジションを使うので、
Adapteeとそのサブ(具象)クラスにも適用可能、多様性あり。

 

 

クラスアダプタ

(多重)継承を使うので、
1つの特定のアダプティに特化したアダプタが実装可能。

オーバライドができる。

 

 

 

いいところ

・Adapterパターンを使わずに、互換性対応のために、
Client側のインターフェースに対する全ての実装を変更する場合、
多くのコード変更やそれによる影響調査などコストが高い

(例)TurkeyインターフェースをDockインターフェースへの互換性対応のために、
Turkeyインターフェースを変更する場合

– Turket.php

interface Turkey
{
-    public function gobble(): void;
+    public function quack(): void;
     public function fly(): void;
}

 

– WildTurkey.php(Turkeyインターフェースを実装する具象クラス)
他にも具象クラスがあれば、
全ての具象クラスのgobbleメソッドの修正が必要となる。

class WildTurkey implements Turkey
{
-    public function gobble(): void
+    public function quack(): void
     {
         echo "ゴロゴロ\n";
     }
 }

 


↓ Adapterを使うと…

1つのクラス内にすべての変更をカプセル化したクラスを提供できる。
Client/Adaptee/TargetInterfaceのコード変更は不要。

 

 

双方向アダプタを作ることも可能

実際に作ってみた!しかし、テキトーすぎて見せたくない。。。

時間あったらちゃんとさせます。

 

– DockAndTurkeyAdapter.php

class DockAndTurkeyAdapter implements DockAndTurkey
{
    /* @var Turkey $turkey */
    public $turkey;

    /* @var Dock $dock */
    public $dock;

    public function __construct($turkeyOrdock)
    {
        if($turkeyOrdock instanceof Turkey)  $this->turkey = $turkeyOrdock;
        if($turkeyOrdock instanceof Dock)  $this->dock = $turkeyOrdock;
    }

    public function quack(): void
    {
        $this->turkey->gobble();
    }

    public function gobble(): void
    {
        $this->dock->quack();
    }

    public function fly(): void
    {
        if($this->turkey !== null){
            for($i = 0; $i < 5; $i++){
                $this->turkey->fly();
            }
        } else {
            $this->dock->fly();           
        }
    }
}

– DockAndTurkey.php
DockインターフェースとTurkeyインターフェースの拡張インターフェース

use HFD\Adapter\Dock\Dock;
use HFD\Adapter\Turkey\Turkey;

interface DockAndTurkey extends Dock,Turkey
{
}

 

interfaceのextendsって初めて使いました。
こうやって使うんですね\(^^)/

 

 

 

 

Commandパターン

 

どうも。
最近、蒙古タンメンにハマりすぎて困っています。
麻婆麺が辛すぎて病みつきになります。

 

 

Commandパターン
を見ていきます。

Commandパターンの定義

リクエスト(命令)をオブジェクトとしてカプセル化し、
その結果、他のオブジェクトを異なるリクエスト、キュー
またはログリクエストなどでパラメータ化でき、
アンドゥ可能な操作もサポートする。
= リクエストをオブジェクトでカプセル化する。
= クライアントはいろんなリクエストを持つことができる。(→使える)

 

クラス図

縦長。。。

 

・Command
…全てのコマンド(オブジェクト)のためのインターフェースを定義する。

 

・ConcreteCommand
…Receiverとaction()の結びつきを(execute()に)定義する。
Invokerがexecute()することで
ConcreteCommandへリクエストを行い、
ConcreteCommandがReceiverの
1つ以上のaction()を呼び出すことでそれを実行する。

 

・Invoker
…コマンド(オブジェクト)を保持し、
ある時点でのコマンドのexecute()を呼び出すことで
コマンド(オブジェクト)にリクエストを実行するよう依頼する。
様々なコマンド(オブジェクト)をパラメタ化して保持できるが、
具体的なコマンド(オブジェクト)の内容は知らない。
(Commandインターフェースを実装している何かだと知っている程度)

 

・Receiver
…制御されるもの(制御対象)

・Client

 

具体的に…

この章でのは
「複数の電化製品のon/offを制御できるリモコン」
でした。

 

PHPで書いたみたコード

 

・Command
…制御したい命令を定義するインターフェース

・LightOnCommand / LightOffCommand [ConcreteCommand]
…具体的に制御対象であるライトの
制御(点けたり消したりする)方法がここにある

・RemoteControl [Invoker]
…制御対象(Receiver)をsetCommandで設定したり、
実際に制御するコントローラー
複数の制御対象をセットできる。

・Light [Receiver]
…制御対象となるライト

・RemoteLoader [Client]

 

いいところ

Invoker(RemoteControl)自体が
制御するReceiver(Light)を知っている場合、
Receiverが増えるたびに、if文で追記する形になってしまう。

RemoteControl.php

  if($receiver == 'Light') {
   $receiver.on();

  } else if($receiver == 'Door') {

   $receiver.open();

- }
+ //制御対象「Sprinkler」の追加
+ } else if($receiver == 'Sprinkler') {
+
+  $receiver.waterOn();
+
+ }

 


↓ Commandパターンを使うと…

 

Invoker(RemoteControl)は
具体的なコマンドオブジェクトの内容を知らずして
(インターフェスは知ってる)、
コマンドオブジェクトに具体的な処理(制御)を依頼できるので、
Invoker(RemoteControl)とReceiver(Light)を分離することができる。
= 各々の役割分担ができる。

RemoteControl.php
※Receiver(LightやDoorなど)について全く出てこない。

use HFD\Command\Command\Command;
use HFD\Command\Command\NoCommand;

class RemoteControl
{
    /**
     * @var Command[]
     */
    public $onCommands;

    /**
     * @var Command[]
     */
    public $offCommands;


    public function __construct(int $commandCnt)
    {
        for ($i = 0; $i < $commandCnt; $i++) { $this->onCommands[] = new NoCommand();
            $this->offCommands[] = new NoCommand();
        }
    }

    public function setCommand(int $slotNum, Command $onCommand, Command $offCommand): void
    {
        $this->onCommands[$slotNum] = $onCommand;
        $this->offCommands[$slotNum] = $offCommand;
    }

    public function onButtonWasPushed(int $slotNum): void
    {
        $this->onCommands[$slotNum]->execute();
        $this->undoCommand = $this->onCommands[$slotNum];
    }

    public function offButtonWasPushed(int $slotNum): void
    {
        $this->offCommands[$slotNum]->execute();
        $this->undoCommand = $this->offCommands[$slotNum];
    }
}

 
制御対象が増える可能性がある時の
拡張性を考えた時に良さそうなパターンですね!
 

 

 

 

 

Factoryパターン〜AbstractFactory〜

Head FirstのFactoryパターンの
例え話が「ピザ屋」のお話だったので、
すんごいピザが食べたくなり、ピザホール一人で食べました。
やっぱりマルゲリータですよね。
んで、追いオリーブオイル。

 

Factoryパターン一族

– Simple Factory[正確にはFactoryパターンではない]
– Factory Method
– Abstract Factory

今回は最後のAbstract Factoryについてです!

 Abstract Factory

オブジェクトを作成をカプセル化し、依存度を下げる。(Factory Methodと同じ)
FactoryMethodと異なり、オブジェクトコンポジションを使います。

具象クラスを指定することなく、
一連の関連オブジェクトや依存オブジェクトを作成するための
インターフェースを提供する。

→ 使い手は、抽象インターフェースを使って、
実際に作成される具体的な製品を知ることなく、
一連の製品を作成できる。

いいところ:
使い手は、具体的な製品の詳細から完全に分離されます。
→ 役割分担できて、個々のクラスの責任が小さくなる

 

クラス図はこんな感じ

 

 

Head Firstでは、
– 工場:ピザの食材を作る食材工場
– 製品:ピザの食材
を例としていました。

こんな感じ(PHPStormのクラス図作れるの教えてもらったので、早速)

PizzaIngredientFactoryクラスで、
関連する一連の食材(Dough, Sauce, Cheese, …)オブジェクトを
まとめてグループ化して作れるのがいいところです。

 

AbstractFactoryのサンプルコード

 

▶︎ PizzaIngredientFactory  ←抽象的な食材工場

interface PizzaIngredientFactory
{
    //実装クラスにピザの食材(製品)の作成方法は任す。
    public function createDough(): DoughInterface;
    public function createSauce(): SauceInterface;
    public function createCheese(): CheeseInterface;
       ...
}

 

▶︎ NYPizzaIngredientFactory (PizzaIngredientFactoryの実装クラス)

↑具象な食材工場

class NYPizzaIngredientFactory implements PizzaIngredientFactory
{
    //具体的なピザの食材(オブジェクト)を作成する。
    public function createDough(): DoughInterface
    {
        return new ThinCrustDough();
    }

    public function createSauce(): SauceInterface
    {
        return new MarinaraSauce();
    }
...

 

▶︎ DoughInterface ←抽象的な食材

interface DoughInterface
{
    public function getName(): string;
}

 

▶︎ ThinCrustDough (DoughInterfaceの実装クラス)

↑具象な食材

class ThinCrustDough implements DoughInterface
{
    private $name;

    public function __construct()
    {
        $this->name = "うっすい生地";
    }

    public function getName(): string
    {
        return $this->name;
    }
}

 

 

ただし、食材が追加された時
Product(MeetInterface, Bacon)の追加だけでなく、
PizzaIngredientFactoryと
PizzaIngredientFactoryを実装する全てのクラスを変更する必要があります。
→影響範囲が大きめです。

例: 肉(食材)を追加する場合

– PizzaIngredientFactory

interface PizzaIngredientFactory
{
    //実装クラスにピザの食材(製品)の作成方法は任す。
    public function createDough(): DoughInterface;
    public function createSauce(): SauceInterface;
    public function createCheese(): CheeseInterface;
   ...
+   public function createMeet(): CheeseInterface;
}

– NYPizzaIngredientFactory

class NYPizzaIngredientFactory implements PizzaIngredientFactory
{
    ...
+   public function createMeet()
+   {
+       //MeetInterfaceの実装クラスをインスタンス化する
+       return new Bacon();
+   }
}

 

 

 

ある程度、固定された一連のオブジェクトがあって、
それらを作成する必要がある時に使えそうですね。

 

 

Factoryパターン〜FactoryMethod〜

そこに、Factoryパターン一族

– Simple Factory[正確にはFactoryパターンではない]
– Factory Method
– Abstract Factory
の章があったので、
ぜひアウトプットしたく書きます!

今回はFactory Methodについてです!

Factory Method

オブジェクトを作成をカプセル化し、依存度を下げるために使います。
(Simple Factory、Abstract Factoryと同じ)

オブジェクト作成のためのインターフェースを
抽象クラス(スーパークラス)に定義しますが、
どのクラスをインスタンス化するかについてはサブクラスに決定させます

→抽象クラス(親クラス)は、
サブクラスの作成したオブジェクトを利用するだけで
具体的なオブジェクトが何なのかを知らない。

AbstractFactoryと違う部分
– 継承を使うので、サブクラス(子クラス)が
作成するオブジェクトの具体的な型を実装する。

 

クラス図でみるとこんな感じ

 

factoryMethodはサブクラスに任せるために、
Factoryクラス(スーパークラス)は抽象クラスになる。

クラス図からもわかるように、
Factoryクラス(スーパークラス)は
Prodoctオブジェクトを作成する具体的な方法は全く知りませんが、
factoryMethodを使えばProdoctオブジェクトを作成できることを知っています。
なので、anOperationメソッド内でfactoryMethodを使用することが多いです。

 

 

HeadFirstでは、
地域スタイルでピザの作り方が異なる
フランチャイズでの例がありました。

FactoryMethodのサンプルコード

 

クラス図はこんな感じ。

 

▶︎ PizzaStore(スーパークラス)
Pizzaオブジェクトを作成する具体的な方法は全く知りませんが、
createPizzaメソッドを使えばPizzaオブジェクトを作成できることを知っています。
→orderPizzaメソッド内でcreatePizzaを使用して実装している。

▶︎ NYPizzaStore(PizzaStoreのサブクラス)
具象Pizza(NYスタイル)オブジェクトを作成する

▶︎ChicagoPizzaStore(PizzaStoreのサブクラス)
具象Pizza(Chicagoスタイル)オブジェクトを作成する

▶︎ Pizza
抽象Pizzaクラス。
PizzaStoreのサブクラスにより、具象Pizzaオブジェクトが作成される。

└ NYCheesePizza 具象Pizzaクラス
└ NYClamPizza 具象Pizzaクラス
└ ChicagoCheesePizza 具象Pizzaクラス
└ ChicagoClamPizza 具象Pizzaクラス

 

メリット1:
地域ごとにピザメニューの作り方が異なっても大丈夫

地域ごとの具象PizzaStoreクラス、具象Pizzaクラスがある。
そのため、同じチーズピザでも、
NY店はゴーダチーズ、Chicago店はモッツアレラチーズにしたり
切り方はサブクラスでオーバライドするなど
多種多様に対応することができる。

 

メリット2:
ChicagoにあるけどNY出身者が多い地区のピザ屋にも対応可能

こんなクラスを作ればいい。
– ChicagoPizzaStore_ManyNewYorker.php

class ChicagoPizzaStore_ManyNewYorker extends PizzaStore
{
    public function createPizza(string $type): object
    {
        $pizza = null;
        if ($type == "チーズ(NY風)") {
            $pizza = new NYCheesePizza();
        } else if ($type == "チーズ(Chicago風)") {
            $pizza = new ChicagoCheesePizza();
        } else if ($type == "野菜(NY風)") {
            $pizza = new NYVeggiePizza();
        } else if ($type == "野菜(Chicago風)") {
            $pizza = new ChicagoVeggiePizza();
        }

        return $pizza;
    }
}

 
ピザ食べたくなりました。
やっぱりマルゲリータですよね。

 
 

Factoryパターン〜Simple Factory〜

この前、domicoのライブが宮崎であるというレアな事象ありました。
行かないわけにはいかないので、行ってきました。
めっちゃ、カッコ良いかったです。

 

早速ですが、私「増補改訂版Java言語で学ぶデザインパターン入門
を読んでますが、並行して楽しく読めると噂のこれも読んでいます。

そこに、Factoryパターン一族

– Simple Factory
– Factory Method
– Abstract Factory

の章があったので、
ぜひアウトプットしたく書きます!

今回はSimpleFactoryについてです!

Simple Factory

正確にはFactoryパターンではない!ですが便利なやつです。

サブクラスが複数存在する場合、
if文などで、各サブクラスをインスタンス化する
→ アプリケーション部分の各部分にif文が分散してしまったり、
保守と更新が困難になったり、実装の間違いが多くなるという問題があります。

 

したがって、
アプリケーション内で、
インスタンス化する部分(変化する部分)と変化しない部分
を分けてカプセル化する
というのがSimpleFactoryです。

 

SimpleFactoryのサンプルコード

こんなな感じかと思います。

 

上記でいうと、SimplePizzaFactory = SimpleFactoryになります。

 

▶︎PizzaStore

SimplePizzaFactoryのクライアント。
ピザをインスタンス取得しますが、
「ギリシャピザやクラムピザなどの具象ピザ」については知る必要がありません。
「pizzaインターフェースを実装したピザ(具象ピザ)を取得し、
prepareメソッド,bakeメソッド,…を呼び出せる」ことを知ってるだけで良い。
ピザを取得する事に関しては、SimplePizzaFactoryに任せるだけでOK!

 

▶︎SimplePizzaFactory

SimpleFactoryの主役。
ピザをの作成方法だけを扱うクラス。
具象Pizzaクラスを参照する唯一の部分。

 

▶︎Pizza

SimplePizzaFactoryの製品であるPizzaの抽象クラス。
具象ピザはこれを実装する。

└ VeggiePizza   具象ピザ(具象製品)
└ PepperoniPizza 具象ピザ(具象製品)
└ ClamPizza    具象ピザ(具象製品)
└ CheesePizza   具象ピザ(具象製品)

新作ピザの追加、ピザメニューの削除があっても。。。

大丈夫!

SimplePizzaFactory.php

- use HFD\SimpleFactory\Pizza\PepperoniPizza;
  use HFD\SimpleFactory\Pizza\ClamPizza;
  use HFD\SimpleFactory\Pizza\VeggiePizza;
+ use HFD\SimpleFactory\Pizza\GreekPizza;

function createPizza(string $type): object
{
        $pizza = null;
        if ($type == "チーズ") {
            $pizza = new CheesePizza();
-       } else if ($type == "ペパロニ") {
-           $pizza = new PepperoniPizza();
        } else if ($type == "クラム") {
            $pizza = new ClamPizza();
        } else if ($type == "野菜") {
            $pizza = new VeggiePizza();
+       } else if($type == "ギリシャ"){
+           $pizza = new GreekPizza();
        }

GeekPizza.php(新作ピザ)

+namespace HFD\SimpleFactory\Pizza;
+
+
+class GreekPizza extends Pizza {
+
+    public function __construct() {
+        $this->name = "ギリシャピザ";
+    }
+
+}

 

ピザの作成を
SimplePizzaFactoryに任せてるので、
SimplePizzaFactoryの修正と
新作ピザクラス(具象ピザ)の追加のみで対応できる。
さらに、PizzaStoreと分けているので
PizzaStoreには影響が少ない!

 

なによりシンプルでわかりやすいですね。

次は、FactoryMethodについて書きたいと思います!

イテレーターを(iterator)を使ってみる

 

今回は、ご存知デザパタの
第1章 Iterator 〜1つ1つ数えあげる〜
について自分なりにまとめたいと思います。

 

イテレーター(iterator)とは

何かの集合体を、数える人
→ 何かの集合体(=aggregate)つまり数える対象が必要
→ 何かの集合体(=aggregate)がいてこその役に立つ

参考:
PHPで書き換えてくれてるソース
https://github.com/HappyDays-jQuery/GoF/tree/master/src/Iterator

 

※PHPでは組み込みで複数のインターフェースがあるので便利!
Iteratorインターフェース
IteratorAggregateインターフェース ..など

何かの集合体(aggregate)と、それを数える人(iterator)を別クラスにすることで、
役割分担ができ、疎結合になる。
→ ただし、aggregateとiteratorを別にする必要がない場合は、
両方の役割をもつIteratorAggregateを使うこともできる。

 

こんな感じ

全体コード→https://github.com/kin29/DezaPata/pull/2/files

– Bookクラス
– BookShelfクラス(=aggregate)
– BookShelfIteratorクラス(=iterator)
– コンテキスト(使う側)

BookShelf(集合体)の中にBook(要素)があり、
BookShelfIteratorが本を一つ一つ数えていく感じです。

BookShelfクラス

<?php

/**
 * 何か(=本)の集合体[Aggregate]
 */
class BookShelf
{
....
    public function getIterator(): BookShelfIterator
    {
        return new BookShelfIterator($this);
    }
}

コンテキスト(使う側)

<?php 

$bookShelf = new BookShelf();
$bookShelf->appendBook(new Book('星の王子様'));
$bookShelf->appendBook(new Book('かいけつゾロリ'));
$bookShelf->appendBook(new Book('ミッケ!'));
$bookShelfIt = $bookShelf->getIterator();

while ($bookShelfIt->hasNext())
{
    echo $bookShelfIt->next()->getName() . "\n";
}

実行すると、appendした本の名前が入れた順番に1つずつ表示されます。

$ php contextIterator.php
星の王子様
かいけつゾロリ
ミッケ!

 

イテレータのいいところ

何かの集合体(=aggregate)や数え方(=iterator)を変更しても、
イテレータを使う側には変更の必要がない。

例)
変更前:数え方→集合体へ要素を追加した順番(FIFO)
変更前:数え方→集合体へ要素を追加した逆の順番(FILO)

<?php 

namespace DP\Iterator; 

/**
 * 何か(=本)の集合体を数える人[Iterator]
 *
 * Class BookShelfIterator
 * @package DP\Iterator
 */ 

class BookShelfIterator {

    private $bookShelf;
    private $index;
  
    public function __construct(BookShelf $bookShelf)
    { 
        $this->bookShelf = $bookShelf;
-       $this->index = 0;
+       $this->index = $this->bookShelf->getLength() - 1;
    }

    public function hasNext(): bool
    {
-       return $this->index < $this->bookShelf->getLength();
+       return $this->index >= 0;
    }

    public function next(): Book
    {
        $book = $this->bookShelf->getBookAt($this->index);
-       $this->index++;
+       $this->index--;
        return $book;
    }
}

実行結果

$ php contextIterator_FILO.php
ミッケ!
かいけつゾロリ
星の王子様

 

イテレータ、便利だしスマートですね!

しかし未だ、これイテレータパターン使えるっ
って言う場面に直面したことがないので、
何か集合体を見つけては、ソースに落とし込むとかして
そういう直感を養っていこうと思います。

デザインパターン入門を読み始めました。〜GoFとは〜

 

有名なやつですね。

存在はもちろん知っていましたが、
難しそうという思い込みで読んでいませんでした。

読まなくては!といういい機会をいただき読み始めました。
入門なのもあり、Javaを知らない私でも意外に読める感じでした!
(思い込みはだめですね。)

 

PHPerな方が、この本と一緒によむといいのが、
デザパタをPHPで書いてくれてるやつ → HappyDays-jQuery/GoF

 

今回は、この本の導入部分のまとめを
自分なりに書いていきたいと思います。

 

GoF(ゴフ)とは

\「GoF」ってよく聞くけど、なんですか?/
という、私の初歩的な疑問がありました。
「はじめに」部分に詳しく書いてくれてました。

開発する上でのよくあるパターンを「デザインパターン」という形で整理したのが、
GoF(ゴフ)と呼ばれた4人だそうです。※GoF = the Gang of Fourの略

もっと詳しく言うと、このGoFにより
オブジェクト指向における再利用のためのデザインパターン
という本がかかれました。
この本では、23個のデザインパターンに「名前」をつけ、
「カタログ」としてまとめられています。

 

–まとめ–
GoF(ゴフ)とは、
デザインパターンを整理してくれた4人の人たちの名称。

 

デザインパターンを学ぶ前に

デザインパターンとは、クラスやインターフェースの関係性である。

デザインパターンの目的は、プログラムを再利用可能なものにすること。
→プログラムを部品として再利用すること
→プログラムを常に、機能拡張や変更を加えていくものとして見る必要がある。

なので、デザインパターンの理解を深めるためには以下を考えると良い。
・どんな機能が拡張される可能性があるか
・その拡張が行われると、修正が必要になるクラスはどれか
・逆に、修正が不要なクラスはどれか

 

また、役割を理解する必要がある。
この本では、デザインパターンをドラマに例えて整理しています。

– 白雪姫(というドラマ) … デザインパターン
– 白雪姫(登場人物) … クラスA
– 王子様(登場人物) … クラスB

白雪姫には、りんごを食べて永遠の眠りについてしまうという役目があるように、
クラスAは役割がある。
また、王子様には、白雪姫にキスをして目覚めさせるという役目があるように、
クラスBには役割がある。
白雪姫が眠らなければ王子様も役目を果たせないように、
し役割をまっとうする必要がある。
登場人物全員が役割を果たしてドラマができあがるように、
デザインパターンもできるという感じでしょうか。

 

次は、第1章Iteratorについてまとめたいと思います!