70
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

シングルトンの抽象化クラス&トレイトを作成する

Last updated at Posted at 2015-02-19

シングルトンパターンの抽象化クラスを作成してみます。まずはシングルトンパターンのおさらいから。

 クラスのインスタンスはnew演算子を使って生成されます。たとえば、5回new演算子を使った場合、5つのインスタンスが生成されます。当然、1000回実行すると1000個のインスタンスが生成されます。
 しかし、インスタンスを生成するという処理は、コストがかかる処理です。オブジェクトの使いまわしをしないで毎回newするのは、大きなコストがかかってしまうことを意味します。
 また、「どうしてもインスタンスを1つしか生成したくない」といった場面も出てきます。たとえば、システムの設定を表現するクラスや、システム全体で一度読み込んだデータをキャッシュしておくクラスなどです。
 この場合、プログラミングする際に注意深くnew演算子を使うことで、1つしかインスタンスを生成させないようにすることもできます。しかし、それは「保証」されたものではありません。当然、何らかのミスや、それを知らない開発者が後からどんどんnewしていってしまうことも考えられます。
 開発者が意識しなくても、あるクラスのインスタンスが1つしか存在しないことを「保証する」ために使われるデザインパターン…それがSingletonパターンです。

Singleton.php(呼び出し先)
class Singleton
{
  // 静的メソッドから呼び出せるのは静的プロパティだけ
  private static $instance;

  // privateにすることで他のクラスからnewできない
  private function __construct()
  {
  }

  // staticにすることでnewしなくてもメソッドが呼べるようにする
  public static function getInstance()
  {
   if (!self::$instance) self::$instance = new Singleton;
   return self::$instance;
  }

  // finalにすることで子クラスから上書きできないようにする
  final function __clone()
  {
   throw new \Exception('Clone is not allowed against' . get_class($this)); 
  }
}
index.php(呼び出し元)

$singleton = Singleton::getInstance();

複数存在しうるEntityなどとは違って、たとえばreopsitoryなどはDBを取り扱う道具ですので、ひとつあれば十分なわけです。じゃぁ各repositoryにgetInstance()を書くかっていわれたら、抽象クラスをつくりたくなりますよね。

repository.php
abstract class Repository 
{
 private static $instance = [];

 private function __construct()
 {
 } 

 public static function getInstance()
 {
   $class = get_called_class();
   if (!isset(self::$instance[$class])) self::$instance[$class] = new $class;

   return self::$instance[$class];
 }

 public final function __clone()
 {
   throw new \Exception('Clone is not allowed against' . get_class($this));
 }
}

テストしてみます。

testRepository.php
require_once('repository.php');
class TestRepository extends Repository
{
}

$test = TestRepository::getInstance();
var_dump($test);  // object(TestRepository)[1]

できました。

(追記)php5.4以上ではtraitを使ったほうがスマートじゃないか、という指摘を頂いたので、

repository.php
trait Repository 
{
 private static $instance = []; 

 private function __construct()
 {
 } 

 public static function getInstance()
 {
   $class = get_called_class();
   if (!isset(self::$instance[$class])) self::$instance[$class] = new $class;

   return self::$instance[$class];
 }

 public final function __clone()
 {
   throw new \Exception('Clone is not allowed against' . get_class($this));
 }
}
testRepository.php
require_once('repository.php');

class TestRepository
{
    use Repository;
}

$test = TestRepository::getInstance();
var_dump($test);  // object(TestRepository)[1]

できました。

70
74
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
70
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?