ためすう

PHP でデザインパターン (Abstract Factory)

2018-10-21

Abstract Factory パターンとは

具体的なクラスを明確にすることなく、関連し合うオブジェクトの集まりを生成するパターン

オブジェクト指向プログラミングでは、具体的なクラスではなくインターフェースや抽象化されたクラスのAPIに対してプログラミングすることが重要になります

Abstract Factory パターンのメリット

  • 具体的なクラスをクライアントから隠蔽する
  • 利用する部品群の整合性を保つ
  • 部品群の単位で切り替えができる

実装例

<?php
interface DaoFactory
{
    public function createItemDao();
    public function createOrderDao();
}

class DbFactory implements DaoFactory
{
    public function createItemDao()
    {
        return new DbItemDao();
    }

    public function createOrderDao()
    {
        return new DbOrderDao($this->createItemDao());
    }
}

class MockFactory implements DaoFactory
{
    public function createItemDao()
    {
        return new MockItemDao();
    }

    public function createOrderDao()
    {
        return new MockOrderDao();
    }
}

interface ItemDao
{
    public function findById($item_id);
}

interface OrderDao
{
    public function findById($order_id);
}

class DbItemDao implements ItemDao
{
    const ITEM_DATA = [
        1 => '商品1',
        2 => '商品2',
        3 => '商品3',
    ];
    private $items;
    public function __construct()
    {
        $this->items = [];

        foreach (self::ITEM_DATA as $key => $val) {
            $item = new Item($key, $val);
            $this->items[$item->getId()] = $item;
        }
    }

    public function findById($item_id)
    {
        if (array_key_exists($item_id, $this->items)) {
            return $this->items[$item_id];
        }

        return null;
    }
}

class DbOrderDao implements OrderDao
{
    const ORDER_DATA = [
        1 => '3',
        2 => '1,3',
        3 => '1,2,3',
        4 => '2',
        5 => '2,3',
    ];

    private $orders;
    public function __construct(ItemDao $item_dao)
    {
        $this->orders = [];
        foreach (self::ORDER_DATA as $key => $val) {
            $order = new Order($key);
            foreach (explode(',', $val) as $item_id) {
                $item = $item_dao->findById($item_id);
                if (!is_null($item)) {
                    $order->addItem($item);
                }
            }
            $this->orders[$order->getId()] = $order;
        }
    }

    public function findById($order_id)
    {
        if (array_key_exists($order_id, $this->orders)) {
            return $this->orders[$order_id];
        }

        return null;
    }
}

class MockItemDao implements ItemDao
{
    public function findById($item_id)
    {
        $item = new Item($item_id, 'dummy item');
        return $item;
    }
}

class MockOrderDao implements OrderDao
{
    public function findById($order_id)
    {
        $order = new Order($order_id);
        $order->addItem(new Item('99', 'dummy item 99'));
        $order->addItem(new Item('99', 'dummy item 99'));
        $order->addItem(new Item('98', 'dummy item 98'));

        return $order;
    }
}

class Item
{
    private $id;
    private $name;

    public function __construct($id, $name)
    {
        $this->id = $id;
        $this->name = $name;
    }

    public function getId()
    {
        return $this->id;
    }

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

class Order
{
    private $id;
    private $items;

    public function __construct($id)
    {
        $this->id = $id;
        $this->items = [];
    }

    public function addItem(Item $item)
    {
        $id = $item->getId();
        if (!array_key_exists($id, $this->items)) {
            $this->items[$id]['object'] = $item;
            $this->items[$id]['amount'] = 0;
        }
        $this->items[$id]['amount']++;
    }

    public function getItems()
    {
        return $this->items;
    }

    public function getId()
    {
        return $this->id;
    }
}

// // DbFactory
$factory = new DbFactory();
// MockFactory
// $factory = new MockFactory();
$item_id = 1;
$item_dao = $factory->createItemDao();
$item = $item_dao->findById($item_id);
//
echo sprintf("ITEM ID: %d Name: %s\n", $item_id, $item->getName());

$order_id = 3;
$order_dao = $factory->createOrderDao();
$order = $order_dao->findById($order_id);

echo sprintf("ORDER ID: %d\n", $order_id);

foreach ($order->getItems() as $item) {
    echo sprintf("ITEM NAME: %s, AMOUNT: %d\n", $item['object']->getName(), $item['amount']);
}

参考

PHP でデザインパターン (Flyweight)

2018-10-20

Flyweight パターンのメリット

一度インスタンス化したオブジェクトを使い回し、生成されるオブジェクトの数やリソースの消費を抑えます

Flyweightパターンでは、メモリ以外のリソースも節約することができます。たとえば、インスタンスを生成する時間がそうです。インスタンスを生成することは、非常に時間がかかる処理の1つです。

実装例

<?php

class Item
{
    private $code;
    private $name;
    private $price;

    public function __construct($code, $name, $price)
    {
        $this->code = $code;
        $this->name = $name;
        $this->price = $price;
    }

    public function getCode()
    {
        return $this->code;
    }

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

    public function getPrice()
    {
        return $this->price;
    }
}

class ItemFactory
{
    const DATA = [
        ['ITEM_CODE_001', 'ITEM_NAME_001', 3800],
        ['ITEM_CODE_002', 'ITEM_NAME_002', 1500],
        ['ITEM_CODE_003', 'ITEM_NAME_003', 800],
    ];

    private $pool;
    private static $instance = null;

    private function __construct()
    {
        $this->buildPool();
    }

    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new ItemFactory();
        }

        return self::$instance;
    }

    public function getItem($code)
    {
        if (array_key_exists($code, $this->pool)) {
            return $this->pool[$code];
        }
        return null;
    }

    public function buildPool()
    {
        $this->pool = [];
        foreach (self::DATA as $row) {
            $item = new Item($row[0], $row[1], $row[2]);
            $this->pool[$row[0]] = $item;
        }
    }

    public final function  __clone()
    {
        throw new RuntimeException();
    }
}

$factory = ItemFactory::getInstance();
$items = [];
$items[] = $factory->getItem('ITEM_CODE_001');
$items[] = $factory->getItem('ITEM_CODE_002');
$items[] = $factory->getItem('ITEM_CODE_003');

// true
var_dump($items[0] === $factory->getItem('ITEM_CODE_001'));

// false
var_dump($items[0] === new Item('ITEM_CODE_001', 'ITEM_NAME_001', 3800));

参考

PHPでデザインパターン (Adapter)

2018-10-20

Adapter パターンとは

  • 「すでに提供されているもの」と「必要なもの」の間の「ずれ」を埋めるようなデザインパターン
  • Adapter パターンは、既存のクラスにはまったく手を加えずに、目的のインターフェース(API)にあわせようとするものです。

Adapter パターンには下記の2種類があります

  • クラスによる Adapter パターン (継承を使ったもの)
  • インスタンスによる Adapter パターン (委譲を使ったもの)

実装例

<?php

class ShowFile
{
    private $filename;

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

    public function showPlain()
    {
        echo file_get_contents($this->filename);
    }

    public function showHighlight()
    {
        highlight_file($this->filename);
    }
}

interface DisplaySourceFile
{
    public function display();
}

// $filepath = __FILE__;
// $show_file = new ShowFile($filepath);
// $show_file->showPlain();
// echo "------\n";
// $show_file->showHighlight();

// 継承を使ったパターン
class DisplaySourceFileImpl extends ShowFile implements DisplaySourceFile
{
    public function __construct($filename)
    {
        parent::__construct($filename);
    }

    public function display()
    {
        parent::showHighlight();
    }
}

$show_file = new DisplaySourceFileImpl(__FILE__);
$show_file->display();

// 委譲を使ったパターン
class DisplaySourceFileImpl2 implements DisplaySourceFile
{
    private $show_file;

    public function __construct($filename)
    {
        $this->show_file = new ShowFile($filename);
    }

    public function display()
    {
        $this->show_file->showHighlight();
    }
}

$show_file = new DisplaySourceFileImpl2(__FILE__);
$show_file->display();

所感

既存のクラスに変更を加えたくない時や、既存のクラスを変更できない場合の選択肢の1つになると思います。

参考

PHP でデザインパターン (Iterator)

2018-10-15

Iterator パターンとは

  • リストの内部構造を隠したまま、それぞれの要素にアクセスさせるためのパターン

  • オブジェクトに対する反復操作をおこなうための統一APIを提供するパターン

Iterator パターンのメリット

  • 利用者に、「どの様な構造を持つリストなのか」を意識させないようにできます。

実装例

<?php

class Employee
{
    private $name;
    private $age;
    private $job;

    public function __construct($name, $age, $job)
    {
        $this->name = $name;
        $this->age = $age;
        $this->job = $job;
    }

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

    public function getAge()
    {
        return $this->age;
    }

    public function getJob()
    {
        return $this->job;
    }
}

class Employees implements IteratorAggregate
{
    private $employees;
    public function __construct()
    {
        $this->employees = new ArrayObject();
    }

    public function add(Employee $employee)
    {
        $this->employees[] = $employee;
    }

    public function getIterator()
    {
        return $this->employees->getIterator();
    }
}

$employees = new Employees();
$employees->add(new Employee('SMITH', 32, 'CLERK'));
$employees->add(new Employee('ALLEN', 26, 'SALESMAN'));
$employees->add(new Employee('MARTIN', 50, 'SALESMAN'));
$employees->add(new Employee('CLARK', 45, 'MANAGER'));
$employees->add(new Employee('KING', 58, 'PRESIDENT'));

$iterator = $employees->getIterator();

while  ($iterator->valid()) {
    $employee = $iterator->current();

    printf(
        "%s (%d, %s)\n",
        $employee->getName(),
        $employee->getAge(),
        $employee->getJob()
    );

    $iterator->next();
}

参考

PHP でデザインパターン (Strategy)

2018-10-14

Strategy パターンとは

Strategyパターンでは、アルゴリズムの部分を他の部分と意識的に分離します。

Strategy パターンのメリット

  • 処理毎にまとめることができる
  • 異なる処理を選択するための条件文がなくなる
  • 異なる処理を動的に切り替えることができる

実装例

<?php

abstract class ReadItemDataStrategy
{
    private $filename;
    public function __construct($filename)
    {
        $this->filename = $filename;
    }

    public function getData()
    {
        return $this->readData($this->getFileName());
    }

    public function getFileName()
    {
        return $this->filename;
    }

    protected abstract function readData($filename);
}

class ReadFixedLengthDataStrategy extends ReadItemDataStrategy
{
    protected function readData($filename)
    {
        // MEMO: 実際は読み込みの処理を実装する
        $data = [];
        $data[] = self::class;
        $data[] = sprintf('このファイルを読み込みます: %s', $filename);

        return $data;
    }
}

class ReadTabSeparatedDataStrategy extends ReadItemDataStrategy
{
    protected function readData($filename)
    {
        // MEMO: 実際は読み込みの処理を実装する
        $data = [];
        $data[] = self::class;
        $data[] = sprintf('このファイルを読み込みます: %s', $filename);

        return $data;
    }
}

class ItemDataContext
{
    private $strategy;

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

    public function getItemData()
    {
        return $this->strategy->getData();
    }
}

$strategy1 = new ReadFixedLengthDataStrategy('dummy1.txt');
$context1 = new ItemDataContext($strategy1);
var_dump($context1->getItemData());

echo "\n";

$strategy2 = new ReadTabSeparatedDataStrategy('dummy2.txt');
$context2 = new ItemDataContext($strategy2);
var_dump($context2->getItemData());

参考

ブログシステムを Github Pages で構築する

2018-10-13

目的

ブログシステムを Github Pages を使い構築しました。

メモがてら、概要と運用してみた感想について書きます。

(具体的な構築手順は、この記事では書きません)

システム構成

シンプルな構成です。

図1.

システム構成-Github Pages

デプロイについて

作業用PC から Jekyll で作成したブログを Github へ Push します。

運用してみて

実際に運用してみました。 作業用 PC (ローカル)でビルドしなくて良いです。

※ 表示確認でビルドすることにはなりますが。

所感

よかったこと

ホスティングするサーバーを借りなく良いので、気軽に始められます。

よくなかったこと

Jekyll のプラグインがセキュリティの観点から Github Pages で動かないものがありました。

作業用 PC (ローカル)でビルド済みのソースコードもバージョン管理すれば、できると思います。

Laravel のサービスプロバイダ、サービスコンテナについて

2018-10-09

目的

Laravel を利用するとき、サービスプロバイダ、サービスコンテナについて、あまり理解していなかったので調べてみました

サービスプロバイダとは

サービスプロバイダは、Laravelアプリケーション全体の起動処理における、初めの心臓部です。

インスタンス化方法を登録します

サービスコンテナとは

Laravelのサービスコンテナは、クラス間の依存を管理する強力な管理ツールです。

インスタンス化の方法が定義されます

ほとんどのサービスコンテナの結合は、サービスプロバイダで行います

DI コンテナみたいなものです

ここで管理することにより、呼び出し元が多数あった場合も

ここを修正すれば良くなりました。

参考

JavaScript の shift と unshift を試す

2018-10-08

目的

JavaScriptのshiftとunshiftの挙動を試しました。

配列の操作(shift unshift)

var numbers = ["one", "two", "three"];
console.log(numbers);

var shifted = numbers.shift(); // 配列の最初の要素を取り除く
console.log(numbers);
console.log(shifted);

numbers.unshift("four"); // 配列の先頭に追加
console.log(numbers);

結果

["one", "two", "three"]
["two", "three"]
one
["four", "two", "three"]

参考URL

Google Chrome 拡張機能のソースコードを見る方法

2018-10-08

目的

Google Chrome 拡張機能のソースコードを解析します

やってみる

拡張機能が保存されているディレクトリ

Mac で保存されている場所

~/Library/Application Support/Google/Chrome/Default/Extensions

拡張機能の ID を調べる

Chrome Web Store で調べます

調べたい拡張機能のページに遷移して URL を確認します

https://chrome.google.com/webstore/detail/adblock/xxxxx?hl=ja

上記の xxxxx の部分が 拡張機能のパスです

参考

unix で自作コマンドの設定をする方法

2018-10-08

目的

unix で自作コマンドを使えるようにしてみます

やってみる

# vim hello

ファイルを作成します

#!/bin/sh

echo "Hello!"

権限を与えます

# chmod a+x hello
# cp hello /usr/bin/hello

コマンドを実行します

# hello

実行結果

Hello

参考URL