2015-04-23 4 views
2

Так что я столкнулся с «проблемой» с классами PHP.PHP-классы - глобальные или __construct

У меня есть несколько классов, которые требуют функций друг от друга, так что на данный момент я делаю следующее:

$db = new blueConnect; 
$core = new blueCore($db); 
$users = new blueUsers($db, $core); 

затем в файле:

public function __construct(blueConnect $db, blueCore $core) { 
    $this->db = $db; 
    $this->core = $core; 
} 

Однако вместо того, чтобы делать это для каждого файла, требующего дополнительных функций, было бы лучше написать

global $db, $core 

в рамках каждой из функций, требующих этого?

+0

Да. Таким образом, вы можете избежать передачи этих параметров. –

+0

Значит, вы оба порекомендовали бы глобальный? Единственная причина, по которой я не хотел ее использовать, заключалась в том, что в одном классе для многих функций требовалось, например, $ db, поэтому использование метода __construct показалось лучшей альтернативой. – zuc0001

ответ

3

Имя Лабиринта, что вы говорите, называется «Dependency Injection» или DI короче.

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

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

public function __construct() { 
    $this->db = blueConnect::getInstance(); 
    $this->core = blueCore::getInstance(); 
} 

Решение этой проблемы, как правило, создать функции Factory, который создает все ваши услуг, которые нуждаются в базе данных и Core.

public function createService($name) { 
    $serviceClass = 'blue'.ucfirst($name).'Service'; 
    return new $serviceClass($this->getDatabase(), $this->getCore()); 
} 

И эта функция, как правило, часть реестра или лучше DI Контейнер как, например PIMPLE

Пример с только один экземпляр каждой службы:

public function createService($name) { 
    $serviceClass = 'blue'.ucfirst($name).'Service'; 

    static $services = array(); 
    if(!isset($services[$name])) { 
     $services[$name] = new $serviceClass($this->getDatabase(), $this->getCore()); 
    } 

    return $services[$name]; 
} 

Имейте в виду, что вы НЕ должны тестировать свой контейнер реестра/DI, поскольку у вас есть "глобальный" состояние внутри вашего контейнера. (например, извлечение одной и той же услуги дважды)

+1

Это [серия статей] (http: // fabien .potencier.org/article/11/what-is-dependency-injection) может дополнять ваш ответ. – tmt

+0

Спасибо за ответ! Я немного смущен тем, как работает фабричная функция.В строке возврата вашего образца кода будет создаваться новый экземпляр класса каждый раз, когда вызывается функция? И где будет размещена такая функция? – zuc0001

+0

Функция обычно является частью ** DI-Container **, так как также должна быть возможность вызвать getDatabase() и т. Д. С PIMPLE есть примеры, как вы могли бы создать только одну Сервис каждого типа и вернуть его, если потребуется второй раз. – chozilla

0

Если параметры являются динамическими и переменными, вы должны передать их в конструкторе, поэтому новый blueUsers($db, $core, ...) будет лучше. Но если это статический параметр, и вам не нужно передавать их при инициализации класса, вы должны определить его как глобальный.

+0

Спасибо за информацию! На данный момент $ db не является статическим и используется для части базы данных, тогда как $ core содержит только статические функции, поэтому должен передаваться $ db через __construct и $ core через global? – zuc0001

+0

Добро пожаловать. Да, точно. – hamed

+2

Альтернативой глобальным может быть фабрика для $ db и $ core (или если они являются одиночными), которые могут быть вызваны изнутри blueUsers, чтобы вернуть существующие экземпляры. –

1

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

final class GlobalObjects 
{ 
    public static function Instance() 
    { 
     static $inst = null; 
     if ($inst === null) { 
      $inst = new GlobalObjects(); 
     } 
     return $inst; 
    } 

    private function __construct() { 
     $db = new blueConnect; 
     $core = new blueCore($db); 
    } 

    public function getDb() { return $this->db; } 

    public function getCore() { return $this->core; } 
} 

// ... 

public function __construct() { 
    $this->db = GlobalObjects::Instance()->getDb(); 
    $this->core = GlobalObjects::Instance()->getCore(); 
} 

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

define("db", new blueConnect); 
define("core", new blueCore($db)); 

$users = new blueUsers($db, $core); 

а потом

public function __construct() { 
    global $db, $core; 

    $this->db = $db; 
    $this->core = $core; 
} 
+0

Мне нравится финальная часть вашего ответа, грязная, но она работает;) Я никогда не думал о включении глобального $ db, $ core внутри __construct! С помощью метода singleton это создает новое db-ядро каждый раз, когда вызывается класс GlobalObjects? – zuc0001

+0

Нет. Синглтон - это объект, который может существовать только один раз (один максимум экземпляра) http://en.wikipedia.org/wiki/Singleton_pattern. Таким образом, новый db создается только один раз и возвращается каждый раз, когда вы вызываете getDb() – ColOfAbRiX

+0

Я озадачен теми константами, которые вы не используете. – tmt

2

Я бы не использовать глобалам.

Причины:

  1. Это просто не OOP путь.

  2. Трудно отлаживать.

  3. Полученный код не подлежит проверке. Если вы хотите, чтобы ваше приложение охватило модульные тесты, вы должны использовать Dependcy Injection, как вы сейчас это делаете. Я попытаюсь кратко объяснить, почему. Модульное тестирование, как следует из его названия, заключается в следующем: тестирование одной единицы вашего приложения, т. Е. Ваших классов (их методы public).

    Теперь предположим, что вы используете глобальные переменные, которые устанавливаются где-то в вашем приложении. Вы не сможете протестировать свой класс blueUsers как автономный блок, потому что вам понадобятся объекты $db и $core. Поэтому вы не сможете просто добавить файл blueUsers.php и протестировать класс, потому что вам понадобятся другие части вашего приложения (те, которые определяют глобальные переменные $db и $core).

    Если с другой стороны вы использовали Dependcy Injection, у вас не возникло бы такой проблемы. Единственное, что вам нужно сделать, чтобы проверить класс blueUsers, это включить класс, создать mocks зависимостей $db и $core и передать их в конструктор blueUsers.

Вот хороший source для более подробного объяснения о том, как писать тестируемый и поддерживаемый код.

И here вы можете найти более подробную информацию о насмешливости и о том, что вы можете извлечь из этого.

Смежные вопросы