Нужна помощь в осознании Registry-паттерна (работа с модулями)

netstuff

Новичок
Нужна помощь в осознании Registry-паттерна (работа с модулями)

Добрый вечер.
Предпосылкой к изучению такого понятия как паттерны стал совет старших товарищей.

С паттерном Singleton вроде разобрался. Если я правильно понял, он применяется в случае постоянного обращения к уникальному объекту.

В моем случае, необходимо реализовать возможность доступа к различным модулям (классам) без использования global. Как я понимаю, паттерн "Реестр", подходит для этого наилучшим образом, так как, в отличии от "Одиночки" хранит список объектов?
Долго изучал этот мануал. Настолько долго, что в какой-то момент времени появилась иллюзия, что я понял. Это была иллюзия =(

Если кто-то изнывает от желания безвозмездно помочь, напишите подробнее, пожалуйста, как работает этот паттерн?
Если же Вы Фанат или HraKK (благодаря которому я и развиваю эту тему), то объясните мне хотя бы, вот это:

PHP:
class Registry {
    var $_cache_stack;
    
    function Registry() {
        $this->_cache_stack = array(array());
    }
    function setEntry($key, &$item) {
        $this->_cache_stack[0][$key] = &$item;
    }
    function &getEntry($key) {
        return $this->_cache_stack[0][$key];
    }
    function isEntry($key) {
        return ($this->getEntry($key) !== null);
    }
    function &instance() {
        static $registry = false;
        if (!$registry) {
            $registry = new Registry();
        }
        return $registry;
    }
    function save() {
        array_unshift($this->_cache_stack, array());
        if (!count($this->_cache_stack)) {
            trigger_error('Registry lost');
        }
    }
    function restore() {
        array_shift($this->_cache_stack);
    }
}
конкретно не понимаю назначение двух последних методов...

Всем огромное спасибо. Это был тест на терпеливость =))))
 

fixxxer

К.О.
Партнер клуба
это стек. там в статье написано, что это нужно для тестирования и почему нужно.

When we come to test our application we are going to want each test to start in a clean state. This is impossible with our current arrangement. If one test class sets up the registry with, say, a fake database connection and is the only one that goes on to use it there will be no problem. If another test is added, and it involves a class that will create a new connection if one does not exist, then we are in trouble. The class will see the previous fake entry and use that instead. This could have all sorts of unforseen consequences invalidating our tests.

And so there is one final workaround we must add for testing. These are and methods to obtain a fresh global registry for the test and then undo the damage immediately afterwoods.
на твоем месте я бы не заморачивался и юзал обычный хэш по имени :)
 

AP

Новичок
netstuff,а что непонятного? ты вообще понимаешь что делают 2 последних метода?
 

netstuff

Новичок
Автор оригинала: AP
ты вообще понимаешь что делают 2 последних метода?
save() - извлекает последний элемент массива и ругаеццо, если они закончились...
restore() - заносит новый элемент массива

netstuff,а что непонятного?
непонятно ВСЁ!!! Если идею, как таковую я вроде понял. Если не ошибаюсь, registry pattern - это singleton pattern, умеющий держать хэш имеющихся классов. Но перенести это на реальную задачу и уж тем более как-то использовать не выходит =(

Вот если бы ты прояснил невежде тонкости работы registry design pattern'а - невежда был бы очень благодарен.

Ещё раз: я пытаюсь достичь возможности работы классов друг с другом без использования global. Большего мне пока и не нужно...

Спасибо.
 

netstuff

Новичок
Автор оригинала: AP
1. Зачем вообще тогда тебе нужен? смотри в сторону делигирования....
кратко о делегировании пожалуйста =)

Паттерн Registry является прекрасной альтернативной использованию одиночек. Registy может заменить целый набор одиночек к случаях, когда внедрение одиночек было связано именно с экономией на инициализации объекта, а также в том случае, если одиночкой был просто “популярный” объект.
- это цитата с первой ссылки. я это ранее читал. читаю и сейчас. поэтому вопрос:

Например, если все клиенты используют Registry для получения часто используемых объектов, то достаточно внедрить LazyLoading для каждого такого объекта. Например:

PHP:
<?php
class AnyRegistry extends Registry
{
  private $_some_object;
  
  function getSomeObject()
  {
    if($this->_some_object)
      return $this->_some_object;
    
    $this->_some_object = new SomeObject();
    
    return $this->_some_object;
  }
}
?>
Даже если требование по наличию только одного экземпляра какого-либо класса класса сохраняется, то конструктор такого класса можно закрыть и продолжать использовать одиночку. Но в целях облегчения тестирования клиенты все же должны получать этот экземпляр только через Registry.
мне это как раз и надо. и то, что написано в качестве примера, на мой взгляд, чистый Singleton. где там Registry я не понял. Смотрим, инициализирован ли класс и если нет - инициализируем. Основной вопрос, как мне перенести это в реальный проект?

Например, есть классы Catalogue, Producer и Menu. Как мне описать эти классы, чтобы я в пределах класса Catalogue мог вывести и Producer и Menu?

Сейчас я мучаюсь с global примерно таким образом:
PHP:
class Catalogue {

function getProducers() {
global $producers;
return $producers->getFullList();
}

}
хотелось бы напрямую обращаться.

[pS] спасибо за ковыряние с моей проблемой, камрад ;)
 

fixxxer

К.О.
Партнер клуба
Автор оригинала: AP
fixxxer, и чём тебе Registry не угодил?
вполне угодил.
PHP:
class Registry extends Singletone {
   private $hash;
   public function get($key) {
      return isset($this->hash[$key]) ? $this->hash[$key] : false;
   }
   public function set($key, $value) {
      $this->hash[$key] = $value;
   }
}
в таком вот простейшем виде я и советую тредстартеру использовать, ибо
В моем случае, необходимо реализовать возможность доступа к различным модулям (классам) без использования global.
когда будут нужны навороты - сам разберется:)

-~{}~ 31.05.07 21:07:

а вообще, netstuff, по-моему тебе нужно что-то вроде фабрики синглтонов ;) но советую не заморачиваться на том, какой паттерн тебе больше подходит, а просто подумать головой, как лучше сделать. будет интересно - потом посмотришь, какой паттерн ты переизобрел :)
 

netstuff

Новичок
fixxxer

Вот налобал мясо:

PHP:
	class Singleton {
		
		protected function __construct() {
		}
		
		static function __instance() {
			static $instance;
			if(!$instance) $instance = Singleton::__instance();
			else return $instance;	
		}
		
		public function test() {
			echo "Hello, World";
		}
		
	}

    class Registry extends Singleton {
    	
    	private $modules;
    	
    	function get($key) {
    		return (isset($this->modules[$key])) ? $this->modules[$key] : false;
		}
		
		function set($key, $object) {
			$this->modules[$key] = $object;	
		}
    	
	}
	
	class Test {
		
		function __construct() {
			Registry::set(get_class($this), $this);
		}
		
		function check() {
			echo "Current registry is: ";
			print_r(Registry::get(get_class($this)));	
		}
			
	}
	
	class innerCallingTest {
	
		function __construct() {
			Registry::set(get_class($this), $this);
		}
		
		function test() {
			Registry::get("Test")->check();
		}
		
	}
	
	$test = new Test();
	$test2 = new innerCallingTest();
	$test->check();
	$test2->test();
в результате получаю следующее:
Current registry is: Test Object ( [modules] => Array ( [Test] => Test Object *RECURSION* ) )
Fatal error: Call to a member function check() on a non-object in /site/modules/core.php on line 54
Ожидал увидеть, если честно:
Current registry is: Array ( [Test] => Object#... , [innerCallingTest] => Object#...)
Current registry is: Array ( [Test] => Object#... , [innerCallingTest] => Object#...)
Что-то не так. Нутром чую :D
 

netstuff

Новичок
Автор оригинала: fixxxer
[Test] => Test Object *RECURSION*

:)
Попробовал сделать так:
PHP:
	class Test {
		
		function __construct() {
		}
		
		function register() {
			Registry::set(get_class($this), $this);
		}
		
		function check() {
			echo "Current registry is: ";
			print_r(Registry::get(get_class($this)));	
		}
			
	}
Не помогло :(

Как же мне занести в хэш ссылку на объект, спрашивается??? :confused:

-~{}~ 31.05.07 22:07:

дальше мысль двинулась в эту сторону, но все равно безрезультатно =(

PHP:
class Test extends Singleton { ...
 

fixxxer

К.О.
Партнер клуба
ну что
начнем сначала с синглтона

PHP:
        static function __instance() {
            static $instance;
            if(!$instance) $instance = Singleton::__instance();
            else return $instance;    
        }
во-первых, включи error_reporting(E_ALL). if(!$instance) здесь некорректно, ибо $instance при первом вызове не определен, надо проверять isset.

во-вторых, тебе не кажется, что тут бесконечная рекурсия?

в общем, http://phpfaq.ru/debug тебе поможет :)
 

dark-demon

d(^-^)b
не просёк, а чем синглетон принципиально отличается от глобала, кроме отложенной инициализации, которая в данном случае ни к селу, ни к городу?
 

leadaxe

Новичок
Замени if(!$instance) $instance = Singleton::__instance();
на
if(!IsSet($instance)) $instance = new Singleton();

или
if($instance==null) $instance = new Singleton();

-~{}~ 01.06.07 00:10:

Автор оригинала: dark-demon
не просёк, а чем синглетон принципиально отличается от глобала, кроме отложенной инициализации, которая в данном случае ни к селу, ни к городу?
Ну в ряде случае удобнtt писать class:: чем через глобал - дополнительная структуризация

-~{}~ 01.06.07 00:15:

Короче пример из мануала ПХП, а то ночь уже =)
PHP:
class Example
{
    // Hold an instance of the class
    private static $instance;
    
    // A private constructor; prevents direct creation of object
    private function __construct() 
    {
        echo 'I am constructed';
    }

    // The singleton method
    public static function singleton() 
    {
        if (!isset(self::$instance)) {
            $c = __CLASS__;
            self::$instance = new $c;
        }

        return self::$instance;
    }
    
    // Example method
    public function bark()
    {
        echo 'Woof!';
    }

    // Prevent users to clone the instance
    public function __clone()
    {
        trigger_error('Clone is not allowed.', E_USER_ERROR);
    }

}
 

dark-demon

d(^-^)b
Ну в ряде случае удобнtt писать class:: чем через глобал - дополнительная структуризация
Код:
global obj;
$obj->do();
$obj->done();
типичное использование глобала

Код:
Class::getInstance()->do();
Class::getInstance()->done();
неэффективно, два раза вызывается одна и та же функция, возвращающая одно и то же. (привет цепочкам! ^_^)

Код:
$obj= Class::getInstance();
$obj->do();
$obj->done();
другое дело, но получаются те же 3 строчки кода.
 

AP

Новичок
dark-demon, ты эффективность строчками мереешь? вытяни всё в одну :)
 
Сверху