Использование на практике активной инверсии зависимостей

seine

Новичок
Использование на практике активной инверсии зависимостей

Привет всем,
вот в статьях про экстремальное программирование, на том же agildev.ru, все так красиво написано, все так понятно и здорово. А я пытаюсь следовать этим требованиям, а у меня какая-то фигня получается :-(
Итак, есть сайт, у сайта есть несколько моделей для работы с различными сущностями, напимер: модель для работы со страницами, статьями, новостями и т.д.
Они все хранятся в папке Models, например, модель для работы со страницами находится в файле Pages.php, а сам класс выглядит так:
PHP:
class Models_Pages {}
Т.е. использую соглашения PEAR, чтоб можно было использовать autoload() для автоматической загрузки.
Например, класс Models_Places_Articles хранится в Models/Places/Articles.php. Ну это я на всякий случай пояснил)
Чтоб получить эти модели из контроллера я использую класс-фабрику, хотя класс - это громко сказано, там просто один метод, вот как он выглядит:
PHP:
class Models_Factory {
     public static function getModel($model) {
         switch ($model) {
             case 'pages':
                 return new Models_Pages(new DB_Mysql(), new Models_Articles(new DB_Mysql()));
                 /* Аналогичным способом возвращаю другие модели */
             default:
                 throw new Exception('Нет модели "' . $model . '"');
         }
      }
 }
Тут использую статичный метод, а это уже нехорошо.
Контроллеров у меня несколько (их точнее было бы назвать компонентами), в них я обращаюсь к нужной модели так:
PHP:
$pages  = Models_Factory::getModel('pages');
$childs = $pages->getChilds($parent_id);
Предыстрию рассказал, теперь о проблеме. Вообще-то, меня такой подход пока что устраивал. Но вот мне кажется, что это вообще не вписывается в рамки XP: есть статичные методы, т.е. явная зависимость от классов. Мало того, так как модели загружаются автоматически с помощью преобразования имени класса в путь, то еще и зависимость от расположения класса в файловой системе (не знаю, как это называется в терминологии ХР, если вообще как-то называется).
Я понимаю, что надо бы, что б компоненты (контроллеры) обращались к моделям не таким кривым способом, а как нибудь при помощи pull approach или push approach методов. Но не могу понять как это сделать(
Есть какие-нибудь идеи?
 

whirlwind

TDD infected, paranoid
Фабрика со статическим методом это плохой подход. Фабрика - узкоспециализорованный класс состоящий из одного метода. Его может понадобиться перегрузить стопицот раз, а у тебя статический вызов. Фабрики связывают классы разных уровней абстракции по всему проекту (например, бизнес-логику и контроллеры).

Статические методы хороши в top-level классах, в таких как например фасады, билдеры или методы доступа к конкретному классу модели. То есть там, где это дает наглядность и удобство. А это может быть только в тех местах, где код однозначно не будет изменяться. Там где классы служат мостами или дверями между другими классами использование статики противопоказано: убивается полиморфизм -> взаимозаменяемость.

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

seine

Новичок
PHP:
class Models_Factory
{
    // Массив с классами, реализующими конкретную модель
    protected $creators = array();


    public function setPageCreator($page_creator) {
        // Устанавливаю класс, который знает, как создать модель страниц
        $this->creators['page'] = $page_creator;
    }

    public function setArticleCreator($article_creator) {
        $this->creators['article'] = $article_creator;
    }

    /* 
     * Тут идут остальные сеттеры 
     * ...
     */

    // Возвращаю требуемую модель
    public function getModel($model) {
        if (key_exists($model, $this->creators)) {
            return $this->creators[$model]->create();
        }
    }
}
Что-то типа этого?
 

whirlwind

TDD infected, paranoid
Я тут не совсем понимаю роль pages. Если это сущность модели, то да - так лучше, только статику можно и оставить (получится реестр фабрик). Если это контроллеры, то для контроллеров обычно своя фабрика, которая может инжектнуть все необходимое в конструктор.

У меня фабрики немного по другому организованы: 1 фабрика на один тип объектов.
PHP:
interface Factory {
	function produce($request);
}
Каждый тип подразумевает сходные требования к инстанцированию. Например, объекты разных моделей требуют впрыск коннекта к БД, контроллеры требуют впрыск фабрики модели и тп. Такие классы могут инстанцироваться одной фабрикой.
 

seine

Новичок
Да, pages - это модель. Можешь показать, как используешь interface Factory? Никак не могу понять.

Автор оригинала: whirlwind
для контроллеров обычно своя фабрика, которая может инжектнуть все необходимое в конструктор
У меня сложно будет передать в конструкторе все необходимое, точнее не сложно, но контроллеров много и каждому из них нужны разные модели, поэтому я решил, что лучше будет, чтоб контроллеры сами обращались к фабрике и получали нужную модель, например, вот как контроллер получал модель страниц:
PHP:
$pages  = Models_Factory::getModel('pages');
Так было раньше, а сейчас я пытаюсь избавиться от статичного метода, т.к. при таком подходе явная зависимость от класса Models_Factory, а хочется абстракции. Душа просит)
 

whirlwind

TDD infected, paranoid
Что значит разные модели? Модель в программе одна. Программа - это и есть модель определенного процесса. Есть элементы модели. Вот их можно инстанцировать через фабрику.

Я от фабрик для классов модели отказался. У меня методы датамаппера реализованы статическими методами, а методы entity обычными методами класса модели. Но раньше я использовал фабрику датамапперов. В приципе разницы особой нет, только подготовка тестового окружения чуть усложняется.

А вот для контроллеров фабрика само то. Вот фронтконтроллер например
PHP:
	function dispatch() {
		$url = $this->getUrl();
		try {
	        if ( $url == '/' ) {
                $this->getFactory()->produce('Index')->defaultAction();
	        } else {
	        	$route = $this->router->findRoute($url);
	        	if ( $route === false ) {
	        		$this->error404();
	        	} else {
	        		$class = ucfirst($route->getMapClass());
	        		$ctrl = $this->getFactory()->produce($class);
	        		$method = $route->getMapMethod();
	        		if ( $method && method_exists($ctrl, $method) ) {
	        			$ctrl->$method();
	        		} else {
	        			$ctrl->defaultAction();
	        		}
		        }
		    }
		} catch ( ClassLoaderException $e ) {
                              ...
            $this->error404();
		}
	}
 

seine

Новичок
Я думал, что моделей может быть много, например, модель гостевой книги, пользователей, страниц сайта и т.д. И каждая модель предоставляет набор определенных действий, которые используют контроллеры. Раз это не модели, то что это?
И еще, что такое entity методы?
Я по твоим постам учусь)
Самое главное, что я понял: весь код, который у меня был, можно выкинуть.
 

whirlwind

TDD infected, paranoid
каждая модель предоставляет набор определенных действий, которые используют контроллеры. Раз это не модели, то что это?
Правильно, это элементы модели. Я вот на это обратил внимание

точнее не сложно, но контроллеров много и каждому из них нужны разные модели, поэтому я решил, что лучше будет
Если у тебя производством однотипных моделей занимается фабрика, а она у тебя это делает - ты указываешь конкретный тип аргументом
PHP:
Models_Factory::getModel('pages');
то с точки зрения контроллера не имеет никакого значения сколько элементов в модели твоей программы. Это как раз не важно. Так же с точки зрения контроллера не важно будет ли доступна модель через статический фабричный или через обычный методы. Важно что любой элемент модели доступен через единый интерфейс. Это именно для контроллеров (или вообще пользователей классов модели).

А вот насколько легко тебе будет контролировать код за этим интерфейсом, вот тут уже имеет значение статический вызов или нет.

entity - объект модели. Есть класс модели, а есть объект этого класса. Например класс город, экземпляр этого класса город Энск. Тут зависит еще от того, как реализовано это отношение одного отдельного экземпляра и самого типа (то есть города Энск и всех остальных городов). Рекомендую начать с паттерна ActiveRecord.
 

seine

Новичок
whirlwind, спасибо за советы) Сорри, что долго не отвечал.
Тему можно закрывать, она потеряла актуальность, да и диалог давно ушел в сторону)
Сейчас решил подучить матчасть, читаю книжку "Быстрая разработка программ" (Роберта Мартина), кстати, классная книга. Как буду готов серьезному созиданю - опять вернусь ;-)
 
Сверху