[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Прошу совета по архитектуре коллекции
hydrogen
Ваяю тут парсер апачевского конфига. Отчасти с целью поупражняться (ибо серьезно именно проектированием раньше не занимался), отчасти для решения практической задачи.

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

Так вот, столкнулся с тем, что класс коллекции изрядно "разбух", что не очень способствует понятности кода.

Методы по назначению можно поделить на несколько групп:
  • Навигация по коллекции (перемещение внутреннего указателя тудым-сюдым, вверх-вниз и т.д.)
  • Работа с общими свойствами элемента, касающимися структуры (getIndex, getPath, getLevel, getParent, size и т.д.)
  • получение ссылок на элементы коллекции (next, prev, current, getChildByIndex и т.п.)
  • операции над элементами коллекции (appendElement, prependElement, pushChild и т.д.)
  • поиск по коллекции
  • разрешение конфликтов (убираем конфликтующие директивы и т.п.)
  • операции над коллекцией (collectionClone, collectionUnset...)

Как бы эту задачку обобщения решить покрасивше?

Последовательно наследовать классы, потихоньку добавляя функционал, на мой взгляд, как-то архитектурно ущербно. Множественного наследования нету. Само собой напрашивается использование типажей (traits), но это привяжет к версии 5.4 (сам сейчас пользуюсь 5.3 и переустанавливать пока желанием не горю). Вроде бы, самый удачный вариант - использовать композицию (особенно в сочетании с сокрытием делегирования). Но тогда в самой коллекции опять таки будет куча делегирующих методов.

На ум напрашивается механизм перегрузки методов. Что, если использовать его для "совсем скрытого делегирования"? Т.е. делегирующие методы не объявлены, а их вызовы обрабатывать при помощи магического метода __call()? А если вызываемого метода нету, то бросать исключение?

Или можно как-то элегантнее проблему решить? Посоветуйте, пожалуйста!

Благодарю!

_____________
hydrogen
Хотя вот это:
Цитата
Работа с общими свойствами элемента, касающимися структуры (getIndex, getPath, getLevel, getParent, size и т.д.)

наверное, логичнее поместить в класс элемента коллекции, и обращаться к этим методам явно?
$collection->currentElement->size();


_____________
sharki
hydrogen
Для такого рода коллекций есть замечательный SPL интерфейс Iterator, посмотри что еще есть в мане
SlavaFr
Цитата (hydrogen @ 14.11.2012 - 09:02)
Методы по назначению можно поделить на несколько групп:

    Навигация по коллекции (перемещение внутреннего указателя тудым-сюдым, вверх-вниз и т.д.)
    Работа с общими свойствами элемента, касающимися структуры (getIndex, getPath, getLevel, getParent, size и т.д.)
    получение ссылок на элементы коллекции (next, prev, current, getChildByIndex и т.п.)
    операции над элементами коллекции (appendElement, prependElement, pushChild и т.д.)
    поиск по коллекции
    разрешение конфликтов (убираем конфликтующие директивы и т.п.)
    операции над коллекцией (collectionClone, collectionUnset...)

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

__call удобная но к сожалению и ужасная конструкция поведение которй не всегда правильно предсказуемо. Также попытка в ide найти несуществующий метод занимает много времени если __call вызывается гдето в parent -классах. Короче будь осторожен с __call



_____________
↓↓↓↓↓↓↓↓↓↓
ответ может быть здесь
или в mysql_error();
hydrogen
sharki, я построил коллекцию на основе SPL класса ArrayObject. Встроенные классы, поддерживающие именно такую коллекцию, какую мне надо (древовидная), мне как-то не попались. Все больше списки, стэки, кучи...

Или я чего не понял?

_____________
hydrogen
Цитата
Если ты уже сам заметил, что методы (тоесть функционал) разделяются на группы, то не стоит ли эти групы разделить на классы?

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

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

То есть, вы советуете не делать делегирующих методов в коллекции, а обращаться напрямую к соответствующим экземплярам классов? Как-то так:

$collection = new Collection();
// бла-бла-бла...
$searcher = new Collection_Searcher($collection);
$directive = $searcher->findDirective('KeepAlive');
// и т.д.
// (при этом $collection->searcher === $searcher)
?

Мол, высокая связность между этими классами обусловлена тем, что они все принципиально взаимосвязаны?

Т.е. в классе самой коллекции лучше оставить только навигацию ну и самые общие вещи?

_____________
hydrogen
Цитата
<pre class="sh_sourceCode" rel="code">// (при этом $collection->searcher === $searcher)</pre>

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

Цитата
__call удобная но к сожалению и ужасная конструкция поведение которй не всегда правильно предсказуемо. Также попытка в ide найти несуществующий метод занимает много времени если __call вызывается гдето в parent -классах. Короче будь осторожен с __call

Кстати, большое спасибо за предостережение!

_____________
sharki
$collection = new Collection();
// бла-бла-бла...
$searcher = new Collection_Searcher($collection);
$directive = $searcher->findDirective('KeepAlive');
// и т.д.
// (при этом $collection->searcher === $searcher)

Ты правильно мыслишь, в любом дереве есть веточка в на ней листочки, вот серчер это веточка. Осталось дело за малым. Кстати тебе бы наверное пригодилась бы логика Одного паииерна, забыл как называется, он еще используется в extjs
sharki
Во, вспомнил, компоновщик называется http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%...BD%D0%B8%D1%8F)
hydrogen
sharki, спасибо за наводочку!

_____________
hydrogen
Да, еще такой вопрос... Допустим, инстанцированы такие экземпляры:

$element_2 {
index -> 0
parent -> $element_1
childs -> array (0 => $element_3)

function remove()
function reindexChilds()
}

$element_3 {
index -> 0
parent -> $element_2
childs -> array ()

function remove()
function reindexChilds()
}


А метод remove() имеет такой вид:


function remove() {
unset($this->parent->childs[$this->index]);
$this->parent->reindexChilds();
}


И $element_2->childs[0] - единственная ссылка на $element_3.

Что произойдет, если вызвать $element_3->remove()? Как поведет себя сборщик мусора? Удалит объект $element_3 по завершению функции $element_3->remove() или это грозит фатальной ошибкой?

Спасибо!

_____________
sharki
да нормально все должно быть, просто линк пропадет.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2025 Invision Power Services, Inc.