読者です 読者をやめる 読者になる 読者になる

PHP7でデザインパターン入門6/23 Prototypeパターン

Prototype パターンとは

生成されるオブジェクトの種別がプロトタイプ(典型)的なインスタンスであるときに使用され、このプロトタイプを複製して新しいオブジェクトを生成する。 このパターンは Abstract Factory パターンでなされるように、クライアント・アプリケーションにおいてオブジェクトの生成者をサブクラスにすることを回避する。 標準的な方法(例えば'new')で新しいオブジェクトを作ることによる固有のコストが所与のアプリケーションにとって高すぎる時にそれを回避する。

Prototype パターン - Wikipedia

具体的には以下のような場合にインスタンスを複製して新しいインスタンスを生成するパターンのようだ。

example

以下サンプルプログラム。 文字列を枠線を囲って表示したり、下線を引いて表示したりするもの。

Framework/Manager クラス

<?php
class Manager
{
    private $showcase = [];

    public function register(string $name, Product $proto)
    {
        $this->showcase[$name] = $proto;
    }

    public function create(string $protoname): Product
    {
        $p = $this->showcase[$protoname];
        return $p->createClone();
    }
}

Framework/Product インターフェース

<?php
interface Product
{
    public function use(string $s);
    public function createClone(): Product;
}

UnderlinePen クラス

<?php
class UnderlinePen implements Product
{
    private $ulchar;

    public function __construct(string $ulchar)
    {
        $this->ulchar = $ulchar;
    }

    public function use(string $s)
    {
        $length = strlen(mb_convert_encoding($s, 'SJIS', 'UTF-8'));
        echo "\"{$s}\"" . PHP_EOL . " ";
        for ($i = 0; $i < $length; $i++) {
            echo $this->ulchar;
        }
        echo PHP_EOL;
    }

    public function createClone(): Product
    {
        $p = null;
        try {
            $p = clone $this;
        } catch (Exception $e) {
            $e->getTrace();
        }
        return $p;
    }
}

MessageBox クラス

<?php
class MessageBox implements Product
{
    private $decochar;

    public function __construct(string $decochar)
    {
        $this->decochar = $decochar;
    }

    public function use(string $s)
    {
        $length = strlen(mb_convert_encoding($s, 'SJIS', 'UTF-8'));

        for ($i = 0; $i < $length + 4; $i++) {
            echo($this->decochar);
        }
        echo PHP_EOL . "{$this->decochar} {$s} {$this->decochar}" . PHP_EOL;
        for ($i = 0; $i < $length + 4; $i++) {
            echo $this->decochar;
        }
        echo PHP_EOL;
    }

    public function createClone(): Product
    {
        $p = null;
        try {
            $p = clone $this;
        } catch (Exception $e) {
            $e->getTrace();
        }
        return $p;
    }
}

main

<?php
$manager = new Manager();
$upen = new UnderlinePen('~');
$mbox = new MessageBox('*');
$sbox = new MessageBox('/');
$manager->register("strong message", $upen);
$manager->register("warning box", $mbox);
$manager->register("slash box", $sbox);

$p1 = $manager->create("strong message");
$p1->use("Hello, world.");
$p2 = $manager->create("warning box");
$p2->use("Hello, world.");
$p3 = $manager->create("slash box");
$p3->use("Hello, world.");

実行結果

"Hello, world."
 ~~~~~~~~~~~~~
*****************
* Hello, world. *
*****************
/////////////////
/ Hello, world. /
/////////////////

役割

  • Product: framework. インターフェース。useとcreateCloneのみ宣言されている。
  • Manager: framework. createCloneを使ってインスタンスを複製するクラス
  • MessageBox: 文字列を枠線で囲って表示するクラス。Productインターフェースを実装している。
  • UnderlinePen: 文字列に下線を引いて表示するクラス。Productをインターフェースを実装している。

まとめ

Prototypeは、インスタンスから新しいインスタンスを複製するパターンである。

ソースコード

github.com

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

PHP7でデザインパターン入門5/23 Singletonパターン

Singleton パターンとは

Singleton パターンを用いると、そのクラスのインスタンスが1つしか生成されないことを保証することができる。ロケールやLook&Feelなど、絶対にアプリケーション全体で統一しなければならない仕組みの実装に使用される。

Singleton パターン - Wikipedia

「このクラスのインスタスはたった1つしか作りたくない」というとき、システムの中に1個しか存在しないものをプログラムで表したい場合に使うことができる。

  • 指定したクラスのインスタンスが絶対に1個しか存在しないことを保証したい
  • インスタンスが1個しか存在しないことをプログラム上で表現したい

example

以下サンプルプログラム。

Singletonクラス

<?php
class Singleton
{
    private static $singleton;

    private function __construct()
    {
        echo("インスタンスを生成しました。" . PHP_EOL);
    }

    public static function getInstance(): Singleton
    {
        if (isset(self::$singleton) === false) {
            self::$singleton = new self;
        }
        return self::$singleton;
    }
}

main

<?php
$obj1 = Singleton::getInstance();
$obj2 = Singleton::getInstance();
echo $obj1 === $obj2
    ? "obj1とobj2は同じインスタンスです。"
    : "obj1とobj2は同じインスタンスではありません。";

実行結果

インスタンスを生成しました。
obj1とobj2は同じインスタンスです。

github.com

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

PHP7でデザインパターン入門4/23 Factory Methodパターン

Factory Method パターンとは

Factory Method パターンは、他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高めることを目的とする。

Factory Method パターン - Wikipedia

インスタンスを生成するfactory(工場)をTempalte Methodパターンで構成したものが、Factory Methodパターンになる。 Factory Methodパターンでは、インスタンスの作り方をスーパークラス側で定めるが、具体的なクラス名は定めない。 具体的な部分は、すべてサブクラス側で行う。これによってインスタンス生成の枠組みと実際のインスタンス生成のクラスを分けて考えることができるようになる。

example

以下サンプルプログラム。身分証明書カード(IDカード)を作る工場を題材としたもの。4つのクラスが登場する

  • Product
  • Factory
  • IDCard
  • IDCardFactory

Product 抽象クラス

<?php
abstract class Product {
    public abstract function use();
}

Factory 抽象クラス

<?php
abstract class Factory {
    public final function create(string $owner): Product {
        $p = $this->createProduct($owner);
        $this->registerProduct($p);
        return $p;
    }
    protected abstract function createProduct(string $owner): Product ;
    protected abstract function registerProduct(Product $product);
}

Productを継承したIDCardクラス

<?php
class IDCard extends Product {
    private $owner;

    public function __construct (string $owner) {
        echo ($owner . "のカードを作ります。" . PHP_EOL);
        $this->owner = $owner;
    }
    public function use() {
        echo ($this->owner . "のカードを使います。" . PHP_EOL);
    }
    public function getOwner(): string {
        return $this->owner;
    }
}

Factoryを継承したIDCardFactoryクラス

<?php
class IDCardFactory extends Factory {
    private $owners = [];

    protected function createProduct(string $owner): Product {
        return new IDCard($owner);
    }
    protected function registerProduct(Product $product) {
        $this->owners[] = $product->getOwner();
    }
    public function getOwners(): array {
        return $this->owners;
    }
}

main

<?php
$factory = new IDCardFactory();
$card1 = $factory->create("member01");
$card2 = $factory->create("member02");
$card3 = $factory->create("member03");
$card1->use();
$card2->use();
$card3->use();
foreach ($factory->getOwners() as $k => $owner) {
    echo "{$k}:{$owner}" . PHP_EOL;
}

実行結果

member01のカードを作ります。
member02のカードを作ります。
member03のカードを作ります。
member01のカードを使います。
member02のカードを使います。
member03のカードを使います。
0:member01
1:member02
2:member03

役割

Productクラス

フレームワーク側。このパターンで生成されるインスタンスが持つべきインターフェースを定める抽象クラス

Factoryクラス

フレームワーク側。Productを生成する抽象クラス。具体的な内容はConcreteCreator役(IDCardFactoryクラス)が定める。

IDCardクラス

具体的な内容を定める側。具体的なProductを定める

IDCardFactoryクラス

具体的な内容を定める側。具体的な製品を作るクラスを定める。

まとめ

フレームワーク(Factory, Product)と具体的な製品を定めるクラス(IDCardFactory, IDCard)に分離することで、全く別の「製品」と「工場」を作る場合にも同じフレームワークが利用できる。

ソースコード github.com

PHP7でデザインパターン入門3/23 Template Methodパターン

Template Method パターンとは

スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を定めるようなデザインパターンのこと。

Template Method パターン - Wikipedia

example

文字や文字列を5回繰り返して表示する、というプログラムをtemplate methodパターンを使って作成する。

AbstractDisplay抽象クラス

<?php
abstract class AbstractDisplay {
    abstract public function open();
    abstract public function print();
    abstract public function close();

    public final function display() {
        $this->open();
        for ($i = 0; $i < 5; $i++) {
            $this->print();
        }
        $this->close();
    }
}

AbstractDisplayを継承したCharDisplayクラス

<?php
class CharDisplay extends AbstractDisplay {
    private $ch;

    public function __construct(string $ch) {
        $this->ch = $ch;
    }
    public function open() {
        echo("<<");
    }
    public function print() {
        echo($this->ch);
    }
    public function close() {
        echo(">>" . PHP_EOL);
    }
}

AbstractDisplayを継承したStringDisplayクラス

<?php
class StringDisplay extends AbstractDisplay {
    private $string;
    private $width;

    public function __construct(string $string) {
        $this->string = $string;
        $this->width = strlen(mb_convert_encoding($string, 'SJIS', 'UTF-8'));
    }
    public function open() {
        $this->printLine();
    }
    public function print() {
        echo("|" . $this->string . "|" . PHP_EOL);
    }
    public function close() {
        $this->printLine();
    }
    private function printLine() {
        echo("+");
        for ($i = 0; $i < $this->width; $i++) {
            echo("-");
        }
        echo("+" . PHP_EOL);
    }
}

main

<?php
(new CharDisplay('H'))->display();
(new StringDisplay("Hello, world."))->display();
(new StringDisplay("こんにちは。"))->display();

実行結果

<<HHHHH>>
+-------------+
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
+-------------+
+------------+
|こんにちは。|
|こんにちは。|
|こんにちは。|
|こんにちは。|
|こんにちは。|
+------------+

役割

AbstractClass(抽象クラス)

テンプレートメソッドを実装する。サブクラスであるConcreteClassによって実装される。

ConcreteClass(具象クラス)

抽象メソッドを実装する。ここで実装したメソッドは、AbstractClassのテンプレートメソッドから呼び出される。

まとめ

スーパークラスで処理の骨組みを規定し、サブクラスで処理の内容を具体化する。 template method を使うことでロジックが共通化できる。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

PHP7でデザインパターン入門2/23 Adapterパターン

Adapter パターンとは

インターフェースが異なっている2つの間に入り、そのずれを埋めるためのパターン。既存のクラスに対して修正を加えることなく、インタフェースを変更することができる。

Adapter パターン - Wikipedia

example

Bannerクラス

<?php
class Banner
{
    private $string;

    public function __construct(string $string) {
        $this->string = $string;
    }

    public function showWithParen() {
        echo "(" . $this->string . ")" . PHP_EOL;
    }

    public function showWithAster() {
        echo "*" . $this->string . "*" . PHP_EOL;
    }
}

PrintInterface

<?php
interface PrintInterface
{
    public function printWeak();
    public function printStrong();
}

PrintBannerクラス

<?php
class PrintBanner extends Banner implements PrintInterface {
    public function printWeak() {
        $this->showWithParen();
    }
    public function printStrong() {
        $this->showWithAster();
    }
}

main

<?php
$p = new PrintBanner("Hello");
$p->printWeak();
$p->printStrong();

実行結果

(Hello)
*Hello*

役割

  • Target: PrintInterface
  • Client: main
  • Adaptee: Bannerクラス
  • Adapter: PrintBannerクラス

まとめ

実際に役立つ場面としては、バージョンアップ(互換性)が挙げられる。 古い版と新しい版とを共存させて、メンテナンスを楽に行うためにAdapterパターンが役立つことがある。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

第219回TOEIC結果

結果

score: 740

  • listening: 400 (-40)
  • reading: 340 (-5)

前回と比較して、listening 440 -> 400, reading 345 -> 340 という結果。

相変わらず点数が伸びない。こうもreadingが伸びないと勉強する意欲も薄れてきてしまう。 今回も最後のトリプルパッセージ問題が時間が足りずに解くことができなかった。

どうしても長文問題が苦手でどうしたものか。

次回は5/21。

laravel 5.4 migration error: [ErrorException] Undefined index: yyyy_mm_dd_hhmmss_create_xxxx_table

laravel で作成したmigrationファイルが不要になったため削除すると、migration時にエラーが出るようになった。

$ php artisan migrate:refresh

  [ErrorException]
  Undefined index: 2017_03_24_082414_xxx_table

上記は、削除したはずのテーブルだが、まだ参照されている様子。

どうやら migrationsテーブルから削除したテーブルを取り除く必要があるようだ。

migrationsテーブルを見てみる

mysql> select * from migrations where '2017_03_24_082414_xxx_table';
+-----+------------------------------------------------+-------+
| id  | migration                                      | batch |
+-----+------------------------------------------------+-------+
| 131 | 2017_03_24_082414_xxx_table           |     1 |
+-----+------------------------------------------------+-------+

削除。

mysql> delete from migrations where migration = '2017_03_24_082414_xxx_table';
Query OK, 1 row affected (0.02 sec)