Консультации по дизайну каркаса - PullRequest
1 голос
/ 03 декабря 2009

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

# 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());

    /* =================== */

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

Ответы [ 3 ]

5 голосов
/ 04 декабря 2009

Не расширять 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, XML. (Я упоминаю только Zend_Config, так как я использовал его и доволен, parse_ini_file также подойдет.)

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


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

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

0 голосов
/ 04 декабря 2009

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

Привет.

0 голосов
/ 03 декабря 2009

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

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

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());

и т.д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...