Тестируемое (небольшое) приложение с базовой инициализацией с использованием внедрения зависимостей - PullRequest
1 голос
/ 13 мая 2011

Все больше и больше применялись надлежащие методологии для написания кода в тестируемой форме, и за последние 2 недели резко изменили мой подход при запуске небольшого приложения с нуля.

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

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

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

Я создаю объект конфигурации, который представляет собой обычный массив с геттерами и сеттерами, одноэлементным соединением БД, объектом логгера и классом error_handling на всей этой фабрике, то есть:

// very sketchy outline of programm flow for initialisation, stripped of error handling 

require_once 'includes/settings.php';
require_once 'AppHelperFactory.php';
require_once 'container.php';

// object to load in container
$appObjects = array = ('log', 'db', 'error_handling');

$appHelperFactory = new AppHelperFactory;

//config object will be needed helper objects
$config = $appHelperFactory->createConfig($settings.php); // 

// create dependency container, sets config object as private property inside,
// container holds only getters for $config.
$container = $appHelperFactory->createContainer($config);
try{      
  foreach($appObject as $className){
     $methodName = 'create' . $className; 
     $container->{$value} = $appHelperFactory->{$methodName}($config);
 }


$app = new ObjectThatWillFinallyGetSomethingDone($container);

$app->doStuff();

Этот подход вызывает вопросы, хотя.Использую ли я инъекцию зависимостей даже близко к правильному способу?лучше ли использовать синглтон DB в контейнере?И то, что мешает мне все время в тестировании, как мне проверить мой "основной" файл?

Ответы [ 2 ]

1 голос
/ 15 мая 2011

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

Поэтому я решил просто пройтись по вашему сообщению сверху вниз и ответить на все вопросыменя это не устраивает.

Соединение с БД с правильно используемым синглтоном

Singletons have no use in PHP

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

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

Опять же: «синглтон», как в «я только создать один, это хорошо.«Синглтон» как и в «Узор» нет.Только не надо, вам это не нужно.

содержит / оборачивает его в объект, который будет передан вместе с другими необходимыми объектами в сотрудничающие классы.

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

Этот Google Tech Talk: Чистые беседы по коду - не ищите! отлично объясняет почему.

Подход, который я рассматриваю, заключается в использовании класса AppFactoryHelper, который действует как очень простая фабрика и создает объекты, отмеченные выше.

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

Теперь о коде

Позвольте мне сначала сказать, что я пытался прочитать эти ~ 30 строк 5 раз, пока не получил зависаниеиз того, что вы пытаетесь сделать, даже если код довольно короткий.Может быть, только я;)

Требуется:

В настоящее время обычно используется автозагрузчик, если вам не нравится наносить ущерб структуре файла, привязывая к нему имена классов, используйте что-то вроде phpab , что всегда для гибкости.

Объекты:

Вы абстрагируете все, что ваше приложение делает с вашей AppHelperFactory.

$appHelperFactory = new AppHelperFactory;
$config = $appHelperFactory->createConfig($settings.php); 
// dunno what $settings.php means here, i assume you mean "settings.php" or something

Я просто предлагаю, чтобы

$config = new Config(); 
$config->readFromFile("settings.php"); 

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

То же самое относится и к другим вашим объектам.

Ваш ObjectThatWillFinallyGetSomethingDone зависит от контейнера, в котором предполагается хранить как минимум 4 объекта, и насмешка над ними для тестирования будет довольно трудной задачей.(Или, по крайней мере, больше боли, чем нужно).

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

Использую ли я инъекцию зависимостей даже близко к нужному способу?

Вы используете реестр.Это что-то еще, но связано.

Читает по этой теме (за и против):

Flaw: Brittle Global State & Singletons (разделы «Добавление или использование реестров» и «Добавление или использование локаторов служб»)

Do you need a Dependency Injection Container?

Dealing with dependencies (только некоторые образцы)

и в некоторой степени связаны Object lifecycle control


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

Так что для этого .. да .. вам удалось это сделать, кроме некоторых мелких жалоб на код

 $container->{$value} = $appHelperFactory->{$methodName}($config); // really? :(

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

Надеюсь, это поможет ... вы также можете перейти к PHP-чату здесь, на SO, для такого рода обсуждений, если вы не можете задать вопрос, потому что это слишком субъективно;)

0 голосов
/ 13 мая 2011

Почему бы вам просто не использовать Symfony2 или другую платформу, соответствующую вашим критериям?Он поддерживает DI, консольные приложения, ведение журналов, подключение к базе данных (используя Doctrine DBAL или ORM) и так далее.Кроме того, его пишут и тестируют сотни человек, поэтому он намного безопаснее и гибче.

  1. Соединение с базой данных не должно быть единичным.Может быть более одного экземпляра соединения.Кроме того, нет необходимости в глобальном доступе с точки зрения приложений, поскольку этот объект используется только некоторыми службами , отвечающими за ведение бизнес-данных.
  2. Используйте взамен отражение $container->{$value}.Намного легче иметь дело с более «статичным» кодом.

EDIT, 2011-05-16

Прежде всего, когда я написал:

Может быть более одного экземпляра соединения.

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

Подключение к базе данных следует рассматривать как службу.Допустим, вы используете PDO , поэтому все, что вам нужно сделать, это определить несколько параметров подключения и создать службу (псевдокод):

...
<parameters>
   <parameter key="db.connection.dns">mysql:dbname=testdb;host=127.0.0.1</parameter>
   <parameter key="db.connection.username">root</parameter>
   <parameter key="db.connection.password">password</parameter>
   <parameter key="db.connection.options" type="collection" />
</parameters>
...
<services>
   <service id="db.connection" class="PDO">
     <argument>%db.connection.dns%</argument>
     <argument>%db.connection.username%</argument>
     <argument>%db.connection.password%</argument>
     <argument type="collection">%db.connection.options%</argument>
   </service>
</services>
...

Так что теперь, когда вы запрашиваете контейнерчтобы вернуть сервис db.connection ($container->getService('db.connection');), он при необходимости создаст и вернет ссылку на объект соединения.

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

<service id="user.manager" class="UserManager">
  <argument type="service">db.connection</argument>
</service>

<service id="thread.manager" class="...">
  <argument type="service">db.connection</argument>
</service>

class UserManager {
  public function __construct(PDO $conn) {
     ...
  }
}

Вот и все.Один объект будет передан всем службам, которым он требуется.

Вы можете использовать DIC Symfony в проекте ZF.Может использоваться как автономный компонент:

  1. http://symfony.com/doc/current/book/service_container.html
  2. http://components.symfony -project.org / зависимость-инъекция / документация
  3. https://github.com/symfony/symfony/tree/master/src/Symfony/Component/DependencyInjection
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...