class Singleton { private static $uniqueInstance = NULL; protected function __construct() {} private final function __clone() {} public static function getInstance() { if (self::$uniqueInstance === NULL) { self::$uniqueInstance = new Singleton; } return self::$uniqueInstance; } }
Этот шаблон гарантирует, что код не будет доступен в приложении, иначе как через метод getInstance(). И клиент не сможет создать объект используя методы new или __clone.
class Client { public function doSomething() { $singleton = Singleton::getInstance(); // ... } }
Очевидно, что невозможно написать тест для метода doSomething() без вызова getInstance(). Это значит, что мы не сможем создать новый объект для каждого из группы тестов и мы не сможем избежать побочных эффектов при тестировании.
Ниже приведены три варианта решения задачи:
Внедрение зависимости (Dependency Injection).
class Client { public function doSomething(Singleton $singleton = NULL) { if ($singleton === NULL) { $singleton = Singleton::getInstance(); } // ... } }
Теперь вместо того, чтобы вызывать метод getInstance() мы можем подменить объект
класса Singletone mock-объектом или заглушкой (Stub).
class ClientTest extends PHPUnit_Framework_TestCase { public function testSingleton() { $singleton = $this->getMock( 'Singleton', /* name of class to mock */ array(), /* list of methods to mock */ array(), /* constructor arguments */ '', /* name for mocked class */ FALSE /* do not invoke constructor */ ); // ... configure $singleton ... $client = new Client; $client->doSomething($singleton); // ... } }
Альтернативная реализация Singletone.
Вместо того чтобы модифицировать клиентский код для облегчения тестирования, мы может немного изменить реализацию одиночки, добавив дополнительный метод для обнуления объекта.
class Singleton { private static $uniqueInstance = NULL; protected function __construct() {} private final function __clone() {} public static function getInstance() { if (self::$uniqueInstance === NULL) { self::$uniqueInstance = new Singleton; } return self::$uniqueInstance; } public static function reset() { self::$uniqueInstance = NULL; } }
Singleton с тестовым контекстом.
class Singleton { private static $uniqueInstance = NULL; public static $testing = FALSE; protected function __construct() {} private final function __clone() {} public static function getInstance() { if (self::$uniqueInstance === NULL || self::$testing) { self::$uniqueInstance = new Singleton; } return self::$uniqueInstance; } }
Выставив Singleton::$testing = TRUE; мы гарантируем, что метод getInstance() будет создавать новый объект при каждом вызове.
Выдержка из публикации
Комментариев нет:
Отправить комментарий