PHP7でデザインパターン入門10/23 Strategyパターン

Strategy パターンとは

Strategy パターンは、コンピュータープログラミングの領域において、アルゴリズムを実行時に選択することができるデザインパターンである。

Strategy パターン - Wikipedia

ようするにアルゴリズムの実装部分をごっそり交換可能にするということのようだ。

example

Strategyパターンを使って、「じゃんけん」プログラムを実装する。

ソースコード

github.com

  • Hand: じゃんけんの「手」を表す
  • Strategy: じゃんけんの「戦略」を表すインターフェース
  • WinningStrategy: 戦略の実装。勝ったら次も同じ手を出す戦略
  • ProbStrategy: 戦略の実装。1回前の手から次の手を確率的に計算する戦略
  • Player: じゃんけんを行うプレイヤーを表す
  • Main: サンプルコードの実行を行う

役割

  • Strategy役: 戦略を利用するインターフェースを定める役。サンプルにおけるStrategyインターフェース
  • ConcreteStrategy: Strategy役のインターフェースを実際に実装する役。サンプルにおけるWinningStrategy, ProbStrategyクラス
  • Context役: Strategyを利用する役。ConcreteStrategy役のインスタンスを持っていて、必要に応じてそれを利用する。サンプルにおけるPlayerクラス

まとめ

  • Strategyパターンでは、アルゴリズムの部分を他の部分と意識的に分離する。アルゴリズムとインターフェースの部分だけを規定する。
  • アルゴリズムを改良したい場合、Strategyパターンを使っていればStrategy役のインターフェースを変更しないように注意しConcreteStrategy役だけを変更すれば良い。
  • 委譲によりアルゴリズムの切り替えが容易

関連パターン

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

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

PHP7でデザインパターン入門9/23 Bridgeパターン

Bridge パターンとは

「橋渡し」のクラスを用意することによって、クラスを複数の方向に拡張させることを目的とする。

Bridge パターン - Wikipedia

この「橋渡し」をする場所は、「機能のクラス階層」と「実装のクラス階層」である。

機能のクラス階層

新しい「機能」を追加したい場合、以下のような階層となる。

SomethingClass
  └ ChildSomethingClass
    └ GrandChildSomethingClass

実装のクラス階層

新しい「実装」を追加したい場合、以下のような階層となる。

SomethingAbstractClass
  └ ConcreteClass
  └ ConcreteClass2

example

以下のサンプルコードは「何かを表示」するためのもの。

DisplyaImpl 抽象クラス

<?php
abstract class DisplayImpl
{
    abstract public function rawOpen();
    abstract public function rawPrint();
    abstract public function rawClose();
}

Display クラス

<?php
class Display
{
    private $impl;

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

    public function open()
    {
        $this->impl->rawOpen();
    }

    public function print()
    {
        $this->impl->rawPrint();
    }

    public function close()
    {
        $this->impl->rawClose();
    }

    final public function display()
    {
        $this->open();
        $this->print();
        $this->close();
    }
}

CountDisplayクラス

<?php
class CountDisplay extends Display
{
    public function __construct(DisplayImpl $impl)
    {
        parent::__construct($impl);
    }

    public function multiDisplay(int $times)
    {
        $this->open();
        for ($i = 0; $i < $times; $i++) {
            $this->print();
        }
        $this->close();
    }
}

StringDisplayImplクラス

<?php
class StringDisplayImpl extends DisplayImpl
{
    private $string;
    private $width;

    public function stringDisplayImpl(string $string)
    {
        $this->string = $string;
        $this->width = strlen($string);
    }

    public function rawOpen()
    {
        $this->printLine();
    }

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

    public function rawClose()
    {
        $this->printLine();
    }

    private function printLine()
    {
        echo "+";
        for ($i = 0; $i < $this->width; $i++) {
            echo "-";
        }
        echo "+" . PHP_EOL;
    }
}

Main

<?php
$d1 = new Display(new StringDisplayImpl("Hello, Japan."));
$d2 = new CountDisplay(new StringDisplayImpl("Hello, World."));
$d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));
$d1->display();
$d2->display();
$d3->display();
$d3->multiDisplay(5);

実行結果

$ php Main.php
+-------------+
|Hello, Japan.|
+-------------+
+-------------+
|Hello, World.|
+-------------+
+----------------+
|Hello, Universe.|
+----------------+
+----------------+
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
+----------------+

ソースコード

github.com

役割

  • Abstraction: 機能クラス階層の最上位クラス。Displayクラス
  • FefinedAbstraction: 改善した抽象化の役。CountDisplayクラス
  • Implementor: 実装者。実装のクラス階層の最上位クラス。DisplayImplクラス。
  • ConcreteImplementor: 具体的な実装者。StringDisplayImplクラス。

まとめ

  • Bridgeパターンの特徴は「機能のクラス階層」と「実装のクラス階層」を分けている点。この2つのクラス階層を分けておけば、それぞれのクラス階層を独立に拡張できる。機能を追加したければ機能のクラス階層にクラスを追加する。このとき、実装のクラス階層は修正する必要がない。さらに、追加した機能は「すべての実装」で利用できる。

  • 例として、OS依存の部分があるプログラムにBridgeパターンを適用することを考える。そのOS依存の部分をBridgeパターンの「実装のクラス階層」で表現し、各OS共通部分のインターフェースを決めてImplementor役とし、ConcreteImplementor役として各OS(win, mac, linuxなど)それぞれのクラスを作る。こうすることによって、「機能のクラス階層」側でいくら機能を追加しても、各OS同時に対応していることになる。

  • 2種類のクラス階層に分離することで、クラス拡張を見通しよく行うことができる。

関連パターン

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

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

pip, pyenv, BeautifulSoup4をインストールする

PythonによるWebスクレイピング」を読んでいる。

本書の実践をする上でPython, BeautifulSoup4 のインストールを行う必要がある。 そのインストール手順の備忘録。

install pip

$ wget https://bootstrap.pypa.io/get-pip.py
$ python get-pip.py

install pyenv

$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv

PATH setting for pyenv

add the below to ~/.bash_profile

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

reload bashrc

$ source ~/.bashrc

install specific python version via pyenv

$ pyenv --version
pyenv 1.0.10-24-gacbd736

# show installable version list
$ pyenv install --list
...
  stackless-3.3.5
  stackless-3.4.1
  stackless-3.4.2

# install specific version from the installable version list
$ pyenv install {version}

# set the version as global
$ pyenv global {version}

$ pyenv version
{version} (set by /home/uorat/.pyenv/version)

# make sure the python version
$ python -V
Python {version}

install beautifulsoup4

$ pip install beautifulsoup4

make sure to complete installing BeautifulSoup

$ python
>>> from bs4 import BeautifulSoup

completed if no error occurs!

PythonによるWebスクレイピング

PythonによるWebスクレイピング

本記事は Qiita に投稿しました。

pip, pyenv, BeautifulSoup4をインストールする on @Qiita http://qiita.com/tic40/items/1806ef4176fea50ae01d

PHP7でデザインパターン入門8/23 Abstract Factoryパターン

Abstract Factory パターンとは

関連するインスタンス群を生成するための API を集約することによって、複数のモジュール群の再利用を効率化することを目的とする。

Abstract Factory パターン - Wikipedia

example

サンプルプログラムは階層構造を持ったリンク集をHTMLファイルとして作る、というもの。 ファイル数が多いため、github上にアップした。

github.com

今までのデザインパターンと比較して、Abstract Factoryはとても込み入っている。

サンプルでは5つの抽象クラスが登場する。

  • Item
  • Link
  • Tray
  • Page
  • Factory

Abstract Factoryパターンでは、抽象的な工場が登場し、抽象的な部品を組み合わせることで抽象的な製品を作り出す。

つまり、部品の具体的な実装には注目せず、インターフェースに注目する。そのインターフェースだけを使って、部品を組み立て、製品にまとめる

役割

  • AbstractProduct: 抽象的な製品。Link, Tray, Pageクラス
  • AbstractFactory: 抽象的な工場。Factoryクラス
  • Client: Main.php
  • ConcreteProduct: 具体的な製品。ListLink, ListTray, ListPage, TableLink, TableTray, TablePageクラス
  • ConcreteFactory: 具体的な工場。ListFactoryクラス, TableFactoryクラス

まとめ

  • 具体的な工場を新たに追加するのは簡単に行える。サンプルコードに工場を追加する場合、Factory, Link, Tray, Pageのサブクラスを作成し、それぞれの抽象メソッドを実装すれば良い。具体的な工場はいくらでも追加できる。

  • 上記と反対に、部品を新たに追加するのは難しい。抽象的な工場Factoryクラスへ、新しいメソッド(新しい抽象的な部品)を定義した場合、既存の全ての工場へ修正が必要となる。これは大変な作業となる。

関連パターン

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

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

PHP7でデザインパターン入門7/23 Builderパターン

Builder パターンとは

オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にする。

Builder パターン - Wikipedia

example

以下は、Builderパターンで文書を作成するサンプルプログラム。

Builder 抽象クラス

<?php
abstract class Builder
{
    abstract public function makeTitle(string $title);
    abstract public function makeString(string $str);
    abstract public function makeItems(array $items);
    abstract public function close();
}

Director クラス

<?php
class Director
{
    private $builder;

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

    public function create()
    {
        $this->builder->makeTitle("Greeting");
        $this->builder->makeString("朝から昼にかけて");
        $this->builder->makeItems([
            "おはようございます。",
            "こんにちは。",
        ]);
        $this->builder->makeString("夜に");
        $this->builder->makeItems([
            "こんばんは。",
            "おやすみなさい。",
            "さようなら。",
        ]);
        $this->builder->close();
    }
}

HTMLBuilder クラス

<?php
class HTMLBuilder extends Builder
{
    private $filename;
    private $writer;

    public function makeTitle(string $title)
    {
        $this->filename = $title . ".html";

        try {
            $this->writer = fopen($this->filename, 'w');
        } catch (Exception $e) {
            $e->getTrace();
        }

        $this->write("<html><head><title>" . $title . "</title></head><body>");
        $this->write("<h1>" . $title . "</h1>");
    }

    public function makeString(string $str)
    {
        $this->write("<p>" . $str . "</p>");
    }

    public function makeItems(array $items)
    {
        $this->write("<ul>");
        for ($i = 0; $i < count($items); $i++) {
            $this->write("<li>" . $items[$i] . "</li>");
        }
        $this->write("</ul>");
    }

    public function write(string $str)
    {
        fwrite($this->writer, $str . PHP_EOL);
    }

    public function close()
    {
        $this->write("</body></html>");
        fclose($this->writer);
    }

    public function getResult(): string
    {
        return $this->filename;
    }
}

TextBuilder クラス

<?php
class TextBuilder extends Builder
{
    private $buffer = "";

    public function makeTitle(string $title)
    {
        $this->append("==============================\n");
        $this->append("" . $title . "\n");
        $this->append(PHP_EOL);
    }

    public function makeString(string $str)
    {
        $this->append('' . $str . PHP_EOL);
        $this->append(PHP_EOL);
    }

    public function makeItems(array $items)
    {
        for ($i = 0; $i < count($items); $i++) {
            $this->append(" ・" . $items[$i] . PHP_EOL);
        }
        $this->append(PHP_EOL);
    }

    public function close()
    {
        $this->append("==============================\n");
    }

    public function getResult(): string
    {
        return $this->buffer;
    }

    public function append(string $str)
    {
        $this->buffer .= $str;
    }
}

Main

<?php
echo "1:plain, 2:html :";

$input = intval(trim(fgets(STDIN)));
if ($input === 1) {
    $textbuilder = new TextBuilder();
    $director = new Director($textbuilder);
    $director->create();
    $result = $textbuilder->getResult();
    echo $result;
} elseif ($input === 2) {
    $htmlbuilder = new HTMLBuilder();
    $director = new Director($htmlbuilder);
    $director->create();
    $filename = $htmlbuilder->getResult();
    echo $filename . "が作成されました。";
} else {
    usage();
    exit;
}

function usage()
{
    echo "Usage: plain プレーンテキストで文書作成";
    echo PHP_EOL;
    echo "Usage: html HTMLファイルで文書作成";
}

実行結果1

$ php Main.php
1:plain, 2:html :1
==============================
『Greeting』

■朝から昼にかけて

 ・おはようございます。
 ・こんにちは。

■夜に

 ・こんばんは。
 ・おやすみなさい。
 ・さようなら。

==============================

実行結果2

$ php Main.php
1:plain, 2:html :2
Greeting.htmlが作成されました。#

ソースコード

github.com

役割

  • Builder: インスタンス生成のためのインターフェースを定める。サンプルではBuilderクラス。
  • ConcreteBuilder: Builder役のインターフェースを実装するクラス。サンプルではTextBuilder,HTMLBuilderクラス。
  • Director: Builder役のインターフェースを使ってインスタンスを生成する役割。Builder役のメソッドのみを利用すr。サンプルではDirectorクラス。

まとめ

  • オブジェクト指向プログラミンでは「誰が何を知っているか」がとても重要。
  • 何を利用しているか「知らない」ということは、「入れ替え可能」ということ。交換可能性(入れ替えができるかどうか)は常に意識して設計を行う必要がある。

関連パターン

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

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

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. /
/////////////////

ソースコード

github.com

役割

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

まとめ

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

関連パターン

増補改訂版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言語で学ぶデザインパターン入門