Singleton(シングルトン)を使ってみる

 

今回は、ご存知デザパタの
第5章 Singleton 〜たった一つのインスタンス〜
について自分なりにまとめたいと思います。

 

シングルトンって音がかわいいですよね、「トン」とかがw

シングルトン(Singleton)とは

何も考えず、実装をしているとなんども同じインスタンスを生成しがちです。
・指定したクラスのインスタンスが絶対1つしか存在しないことを保証したい
・インスタンスが1つしか存在しないことを、プログラム上で表現したい

そんなときに、インスタンス生成コストを下げるためにも、
Singletonパターンを使います。

 

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

 

 

こんな感じ

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

– Singletonクラス
– コンテキスト(使う側)

Singletonクラス

<?php

namespace DP\Singleton;

class Singleton
{
    /**
     * @var Singleton
     */
    //static宣言がされているため、
    //クラスのインスタンス化の必要なしにアクセスできる。
    //ただしprivateなので同クラス内のからしかアクセスできない
    private static $singleton;

    //privateなので、同クラス内からしかインスタンス生成をできない。
    //他のクラスからnew Singletonをするとエラーになる。
    //このクラスではgetInstanceがインスタンス生成をしている。
    private function __construct()
    {
        echo "create SingletonClassInstance\n";
    }

    //すでに$singletonにインスタンスがセットされている場合は、newしない。
    public static function getInstance()
    {
        //$singletonがstatic宣言されているため、
             //$this->singletonでアクセスできない。
        if (is_null(self::$singleton)) {
            self::$singleton = new Singleton();
        }
        return self::$singleton;
    }
}

 

 

contextSingleton.php ←コンテキスト(使う側)

<?php

use DP\Singleton\Singleton;

echo "Singletonクラスを普通にnewしてみる。\n";
new Singleton(); //たぶんおこられる。

外部クラスからnewすると
Singleton::__construct()がprivateなので怒られます。

$ php contextSingleton.php

Singletonクラスを普通にnewしてみる。
PHP Fatal error: Uncaught Error: Call to private DP\Singleton\Singleton::__construct() from invalid context in /Users/XXXXX/DezaPata/contextSingleton.php:10
Stack trace:
#0 {main}
 thrown in /Users/XXXXX/DezaPata/contextSingleton.php on line 10

 

 

contextSingleton.php ←コンテキスト(使う側)

<?php

use DP\Singleton\Singleton;

echo "getInstanceしてみる(instance1)\n";
$instance1 = Singleton::getInstance();
echo "もう一度、getInstanceしてみる(instance2)\n";
$instance2 = Singleton::getInstance();
echo "2回目のgetInstanceはインスタンス生成を行わないため、constructを通らないはず\n";
echo "(instance1 === instance2)なのか確認\n";
echo ($instance1 === $instance2) . "\n";

$ php contextSingleton.php

getInstanceしてみる(instance1)
create SingletonClassInstance
もう一度、getInstanceしてみる(instance2)
2回目のgetInstanceはインスタンス生成を行わないため、constructを通らないはず
(instance1 === instance2)なのか確認
1

Singletonクラスのインスタンスを得ることができるgetInsranceメソッドを
2回実行してみる。
1回目でインスタンス生成によりコンストラクラにて初期化でいを行い、
2回目はインスタンス生成していないことがわかる。
さらに、1回目と2回目のインスタンスは同じであることもわかる。

→ Singletonクラスは1つしか存在せず、
getInstanceで毎回同じSingletonクラスのインスタンスを得ることができました!!!

 

<<追記 2019-03-12>>

ただし、このままの方法ではタッチの差で、

if (is_null(self::$singleton))

判定をした場合には
インスタンスが1つ以上作られる可能性があります。

この対策としては二つがあります。
●同期化
●初期化時点でインスタンスを生成し、
getInstanceでは常にそのインスタンスを返すようにする。

class Singleton
{
    private static $singleton = new Singleton();

    private function __construct()
    {
        echo "create SingletonClassInstance\n";
    }

    public static function getInstance()
    {
        return self::$singleton;
    }
}

 

 

static宣言

HogeClass.php

class HogeClass
{
    public static $staticProperty = "staticなプロパティです。\n";

    public static function staticMethod()
    {
        return "staticなメソッドです。\n";
    }
}

 

staticなメソッド、staticなプロパティともに
インスタンス化せずとも外部からアクセスが可能である。

echo HogeClass::$staticProperty;  //staticなプロパティです。
echo HogeClass::staticMethod();   //staticなメソッドです。

 

HogeClass内からのアクセスもself::で行う。
$this->で行うと、エラーとなる。

PHP Fatal error: Uncaught Error: Using $this when not in object context in

 

※注意!

staticなプロパティはインスタンス化されたクラスオブジェクトから
アクセスすることはできない(static なメソッドにはアクセスできます)。

$HogeObj = new HogeClass;
echo $HogeObj->staticProperty;
//PHP Notice:  Accessing static property HogeClass::$staticProperty as non static in...
echo $HogeObj->staticMethod();   //staticなメソッドです。