Рекомендации по проектированию каркаса

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

# Basically it's just a Registry pattern class Repository { private static $objects = array(); public function loadObject($alias, $object) { self :: $objects[$alias] = $object; return true; } public function __get($name) { if ($this->objectExists($name)) { return self::$objects[$name]; } else { return false; } } } class Database extends Repository { /* database class */ } class Session extends Repository { public function some_func($key, $value) { /* i can access database object using $this in any class that extends Repository */ $this -> database -> exec (/* sql */); } } /* =================== */ # Load core objects $R = new Repository :: getInstance(); $R -> loadObject ('config', new Config()); $R -> loadObject ('database', new Database()); $R -> loadObject ('session', new Session()); /* =================== */ 

Можете ли вы увидеть какие-либо проблемы или недостатки такого подхода? Для меня я вижу, возможно, немного больше потребления памяти, потому что каждый следующий класс содержит все больше и больше объектов из репозитория. Прежде, чем у меня была конструкция, в которой каждый класс был независимым, но в любом случае все они требуют базы данных, сессии, конфигурации и т. Д., Я не должен был объявлять их в любом классе. Просто хочу отметить, что я планирую этот проект только для основных объектов, а не для определенных классов.

Solutions Collecting From Web of "Рекомендации по проектированию каркаса"

«потому что каждый следующий класс содержит все больше и больше объектов из репозитория» – я не совсем понимаю, что вы подразумевали под этим, я думаю, что, поскольку объекты являются статичными, есть только одна копия.

Я думаю, вы можете использовать немного другой подход, чтобы избежать недостатков, объединив одноэлементный шаблон.

 class Repository { private static $instance; private $objects = array(); private static getInstance() { if (!Repository::$instance) !Repository::$instance = new Repository(); return !Repository::$instance(); } public static function loadObject($alias, $object) { Repository::getInstance()->objects[$alias] = $object; return true; } public static function get($name) { $repository = Repository::getInstance(); if (isset($repository->objects[$name] return $repository->objects[$name]; else return false; } 

Затем вы будете использовать это в своих дочерних классах:

 Repository::get('config'); 

и в бутстрапе

 Repository::loadObject('config', new Config()); Repository::loadObject('database', new Database()); 

и т.п.

Не распространяйте Repository :

  • База данных не является репозиторием, в репозитории имеется база данных
  • Ваша база данных / сеанс / конфиг не связаны и не должны быть. Принцип замены Лискова :

[…], если S является подтипом T, тогда объекты типа T в программе могут быть заменены объектами типа S без изменения каких-либо желательных свойств этой программы (например, правильность).

Изменить: попытка ответить на последующие вопросы в этом ответе.

Этот метод называется инъекцией зависимостей. Пример сеанса:

 class Session { // notice the clean API since no methods are carried along from a possibly huge base class public function __construct(ISessionStorage $storage) { $this->_storage = $storage; } public function set($key, $value) { $this->_storage->set($key, $value); } } interface ISessionStorage { public function set($key, $value); } class DatabaseSessionStorage implements ISessionStorage { public function __construct(Db $db) { $this->_db = $db } public function set($key, $value) { $this->_db->query("insert...."); } } class CookieSessionStorage implements ISessionStorage { public function set($key, $value) { $_SESSION[$key] = $value; } } // example where it's easy to track down which object went where (no strings used to identify objects) $session = new Session(new DatabaseSessionStorage(new Db())); $session->set('user', 12512); // or, if you'd prefer the factory pattern. Note that this would require some modification to Session $session = Session::factory('database'); $session->set('user', 12512); 

Конечно, вы можете сохранить настройки подключения, жестко закодированные в конфигурационном файле. Это означает, что другим файлам необходимо овладеть этим классом конфигурации, не проходя своих родителей. Например:

 class Database { // The same pattern could be used as with the sessions to provide multiple database backends (mysql, mssql etc) through this "public" Database class public function __construct(Config $config) { $this->_config = $config; $this->_connect(); } private function _connect() { $this->_config->getDatabaseCredentials(); // do something, for example mysql_connect() and mysql_select_db() } } 

Если вы предпочитаете сохранять конфигурационную информацию из php-файлов (для упрощения редактирования / чтения), см. Zend_Config для примеров доступа к различным устройствам хранения, включая более распространенные: ini, php array, xml. (Я только упоминаю Zend_Config, так как я использовал его и доволен, parse_ini_file тоже будет работать.)

Хороший и, надеюсь, легкий читать: Fabience Potencier – Что такое инъекция зависимости?


Редактировать # 2:

Также см. Слайд: Мэтью Вейер О'Финни – Архитектура ваших моделей

Я думаю, что это плохой пример. Невозможно сохранить объект, такой как конфигурация, база данных и сеанс во внутреннем массиве. Эти объекты должны быть явными (возможно, статическими) свойствами в вашем базовом классе и должны быть доступны точным именем. Пурист, вероятно, сказал бы, что доступ к массиву также медленнее, чем прямой доступ к свойствам :-)) Я бы реализовал эти объекты как одиночные и, возможно, спрятал их в чем-то вроде класса Application. Я думаю, что переопределение __get () подходит для объектов с динамическими свойствами или когда вы хотите предотвратить доступ к несуществующим свойствам (просто исключение для исключения). Это также помогает (побочный эффект) от повышения уведомлений PHP (я их ненавижу :-)), когда какое-либо свойство не определено, но уведомления должны быть убиты явно, потому что они (маленькие, но …) просто ОШИБКИ! Хранить во внутренних свойствах массива, относящихся к определенным объектам (с десятками свойств). Я интенсивно использую это решение в классах, сопоставленных с записями таблицы db.

С уважением.