LoginSignup
26
29

More than 5 years have passed since last update.

Singletonのデザインパターン

Last updated at Posted at 2015-05-14

Singleton

GoFが考案した23種類のデザインパターンの一つです。
”Single”とあるように、複数のインスタンスを生成させないためのデザインパターンです。

Singletonを使わない例

共通の例として、クラス内部のPDOでデータベースに接続するクラスを作成しました。

Singletonを使わない場合

<?php

class DB
{
    /**
     * @var PDO
     */
    private $pdo;

    private $conf = [
        'host' => 'localhost',
        'db' => 'test',
        'user' => 'user',
        'password' => 'password',
        'charset' => 'utf8'
    ];

    public function __construct()
    {
        $dsn = "mysql:host={$this->conf['host']};dbname={$this->conf['db']};charset={$this->conf['charset']};";
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ];

        $this->pdo = new PDO($dsn, $this->conf['user'], $this->conf['password'], $options);
    }

    public function find($id){
        //.....
    }

}

$dbs = [];
for ($i=0; $i<5; $i++) {
    //ループごとにMySQLのコネクションが増える
    $dbs[$i] = new DB();
    $dbs[$i]->find(1);
}

最後の部分でループの中で毎回インスタンスを生成するような処理を入れています。
これでは、最終的に5個のインスタンスが生成されて、MySQLにも5個のコネクションができてしまいます。

Singletonを使ったクラス

<?php

class DB
{
    /**
     * @var PDO
     */
    private $pdo;

    private $conf = [
        'host' => 'localhost',
        'db' => 'test',
        'user' => 'user',
        'password' => 'password',
        'charset' => 'utf8'
    ];

    //コンストラクタをprivateにするのが肝
    private function __construct()
    {
        $dsn = "mysql:host={$this->conf['host']};dbname={$this->conf['db']};charset={$this->conf['charset']};";
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ];

        $this->pdo = new PDO($dsn, $this->conf['user'], $this->conf['password'], $options);
    }

    //外部からインスタンスを呼び出したい場合のメソッド
    final public static function getInstance()
    {
        static $instance;
        return $instance ?: $instance = new self;
    }

    //クローンを禁止する
    final public function __clone()
    {
        throw new Exception("this instance is singleton class.");
    }

    public function find($id){
        //.....
    }

}

$dbs = [];
for($i = 0; $i < 5; $i++){
    $dbs[$i] = DB::getInstance();
    $dbs[$i]->find(1);
}

ポイント

  1. コンストラクタがprivateメソッドになっている
  2. 外部からはDB::getInstanse()でインスタンスを取得する
  3. インスタンスのcloneの作成を禁止

コンストラクタをprivateメソッドにしていることで、クラスの外からnew DBと新しいインスタンスを生成できなくなっています。
外部からインスタンスを取得するときはDB::getInstanse()を使い、初回時に$instance = new selfでクラス内から自身のインスタンスを作成しています。
また、インスタンスのコピー(クローン)もできないよう、マジックメソッドで例外を発生させるようにしています。

トレイトを使ったクラス

トレイトはPHP5.4から使えるようになりました。

<?php

trait Singleton
{
    //コンストラクタはprotected
    protected function __construct(){}

    //さっきと同じ
    final public static function getInstance()
    {
        static $instance;
        return $instance ?: $instance = new static;
    }

    //さっきと同じ
    final public function __clone()
    {
        throw new Exception("this instance is singleton class.");
    }
}

class DB
{
    //トレイトを使用する宣言
    use Singleton

    /**
     * @var PDO
     */
    private $pdo;

    private $conf = [
        'host' => 'localhost',
        'db' => 'test',
        'user' => 'user',
        'password' => 'password',
        'charset' => 'utf8'
    ];

    //トレイトのコンストラクタを上書き
    protected function __construct()
    {
        $dsn = "mysql:host={$this->conf['host']};dbname={$this->conf['db']};charset={$this->conf['charset']};";
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ];

        $this->pdo = new PDO($dsn, $this->conf['user'], $this->conf['password'], $options);
    }

    public function find($id){
        //.....
    }

}

$dbs = [];
for($i = 0; $i < 5; $i++){
    $dbs[$i] = DB::getInstance();
    $dbs[$i]->find(1);
}

ポイント

  1. use SingletonでSingletonトレイトの使用を宣言する
  2. Singletonの骨格はトレイトの方に記述
  3. DBクラスでコンストラクタが使えるように、トレイトコンストラクタをprotectedにしている

「Singletonを使ったクラス」をDBクラスとSingletonトレイトの2つに分け、Singletonのパターンを他のクラスでも作成できるよう、トレイトに纏めて、使用するクラスの時にuse Singletonを宣言しています。

26
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
29