Pull to refresh

Comments 60

А почему бы не унаследовать класс, от того, поведение которого мы хотим изменить и использовать уже дочерний?
Ну если хочется изменить поведение тридцати классов которые наследуют от одного класса, то ваше решение не выглядит тривиальным :) Особенно когда это разбросанно по десяткам модулей. особенно когда ты в принципе не знаешь где оно используется. (К примеру ты пишешь не модуль для своего конкретного проекта, а универсальный модуль который будет добавляться в любой проект).
Вообще конечно такие ситуации в принципе печаль. Но так да, костыли и хаки.
Ну почему костыль? Вот в данный момент я пишу фреймворк, который в первую очередь будет заточен под электронный документооборот. В тестовом приложении у меня будет около двух десятков видов документов, все они будут наследовать от класса document.
Допустим у нас будет стоять задача вести учет версий документов с сохранением автора версии.
По большому счету такая задача решается довольно тривиальным изменением в CRUD модели.
Можно добавить еще дополнительный view, но можно уже и без этого…

Или на этапе эксплуатации системы вдруг оказывается, что значительная нагрузка на базу приходится на запросы типа «какие пары кладовщик/продавец работающие в один день создают максимальное количество документов относительно количества проданных товаров» (вполне реальная эвристика для поиска некоторых афер со скидками и т.п.). И мы хотим для ускорения таких отчетов добавить к методу создания документа (ЛЮБОГО) чтобы он изменял некоторые предварительно рассчитанные коэффициенты. Это просто решается — мы просто добавляем нужный код в метод create нашего универсального класса document.

Если вы предложите более красивую архитектуру которая позволит решать такие задачи, то я с удовольствием выслушаю вашу идею. Но сколько я не искал — всё скатывается к банальным hook или событийной модели с перехватами событий…
А как у вас создается новый документ?
$doc = new Document();
$doc->create();

Так?
Ну примерно. Document вообще-то абстрактный. И с ORM чуть иначе работаем, но не суть, пусть будет:
class Prikaz extends Document {
// допольнительная реализация
}
$doc = new Prikaz();
$doc->create();
Да, понятно что сам Document не используется на прямую. Я просто о сути. Так вот, я думаю что поскольку объект описывает документ, то имеет смысл логику создания документа завернуть в конструктор. То есть после
$doc = new Prikaz();

Мы уже получаем документ и работаем с этим объектм, без вызова дополнительного метода. А поскольку у нас предполагается возможность вносить какие-либо модификации и/или предварительные расчеты в процессе его создания, то можно передвать в конструктор объект-мутатор (так оно кажется назвается =) ), который будет уметь работать с определенным типом документа(-ов).
Только нужно место, где будет происходить определение того какие мутаторы использовать. Таким образом просто меняем объеткы в зависимости от ситуации и документы нужным нам образом модифицируются или еще какието действия производятся в процессе создания.
мутаторы вместо классов?
т.е. для каждого вида документа делать мутаторы а работать чисто с документами?
Ну так там и работа с базой, и структура и куча вьювов и куча бизнеслогики… и опять таки это частный случай. Я должен специально усложнить структуру ветки классов «Документы» для того чтобы потом их можно было бы переопределять… но не всегда заранее можно предсказать что другой человек на другой стороне планеты спустя пять лет после выхода проекта захочет переопределить.

Если же использовать мутаторы для наших изменений, то опять таки это не сильно далеко уйдет от первоначального варианта — переопределять потомков. Все тридцать. В чужих модулях. Которых я в глаза не видел. ага.
То есть проблема в том, что уже существует большая система и затраты на такой рефакторинг слишком велики?
Ну так кстати говоря 30 класс это вобщем-то не много и современные IDE вполне безболезненно справились бы с изменением сигнатуры конструкторов.
А какого хрена какая-то сволочь будет править конструкторы в моих классах? Он что охренел чтоль? А потом ко мне еще за поддержкой обращаться будет. Ага.
Ну да фиг с ним. Если у него руки растут из того места и он осилил ваши трехэтажные решения то думаю он особенно ничего не испортит. Но вот кого я не пущу в свой код (ладно свой, но еще дюжины независимых разработчиков разных модулей) так это системного администратора клиента которому пришлют вместо изящного модуля и инструкции прописать две строки в одном файле — инструкцию о том как делать рефакторинг в IDE. Увольте…
Я это к тому, что затраты не такие и большие на внедрение данного решения… ладно.
Каждого конкретного решения? Не кажется ли вам что именно это и является костылями — разрастание кода, усложнение процесса установки и настройки ради «правильности» кода?
У меня на этапе проектирования фреймворка уже есть пару дюжен идей что и как стоило бы расширить. А сколько еще таких вещей придумают другие?
Да в чем усложнение то? Просто отдельные объекты со своим поведением, котроые ни от кого не зависят и от них тоже никто не зависит. Передали в конструктор — хорошо, используем. Не передали — ну и ладно, просто создаем документ.

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

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

Молодцы блин! Документация выросла в два раза, код в полтора, кодеры нихрена не понимают что это и зачем… Все ждут очередного стопятисотого релиза в котором наконец появится возможность мутировать класс разбора ссылок, чтобы модуль мультиязычности не требовал правки кода ядра…
Все равно десяток мест не предусмотрели где подготовить под мутации, и их хакают по черному. Но зато у нас "правильный код". И при этом мы можем хвастать что мол у нас «нет проблем с архитектурой».

Мне вас не понять — я месяц ходил локти кусал прежде чем решился таки на плейсхолдеры в конструкторе запросов. Банальные плейсхолдеры. А тут хуки, обозреватели, мутации… и без проблем.
Даже боюсь спрашивать что плохого в плейсхолдерах))
Но причем тут ОРМ, вьюхи и прочее описаное. Речь ведь всего лишь о документах и некотрой динамической логике во время их создания…
Нет, если вы внимательно читали заметку, то в самом начале речь шла как раз об универсальном решении поскольку заранее совершенно не понятно что именно захочется изменять.
Вот есть у меня класс dummy_view который выполняется если вдруг не было найдено никаких вьювов для класса и он не может быть обработан даже универсальным вьювом для итераторов. Он выводит информацию очень коряво, поскольку строго говоря это нештатная ситуация и такой вывод нужен скорее для отладки.
Но кто-то вдруг решил изменить его поведение. Ну пусть даже в лог при этом что-то писать… откуда я знаю что он там себе захотел? Его тоже мутировать?))))

В плейсхолдерах плохо то, что это форма записи которая не интуитивнопонятна. Мне пофиг в какой форме писать, мне и обратная польская запись вполне родная, благо МК-52 в детстве был… Но другим не понятна, и я с болью в сердце пошел на это усложнение синтаксиса в пользу безопасности и надежности.
Эм… это же просто подстановки. Да и PDO уже не первый день существует, если конечно речь о нем.
Проблема в том, что я сейчас в 2012 не могу (да и не хочу) предсказывать ВСЕ возможные рефакторинги которые могут понадобится в 2032.
> Prikaz

Ну я прошу… ну не надо.
В реальном примере вообще Nakaz :)
Но я решил не травмировать психику…
Если упрощённо:

$doc = new Document();
$doc->onCreate(array($docCache, 'recalculateSmth'), array($doc));
$doc->create();


А вообще, если хотите подменить какой-то класс — используйте DI. Если фреймворк вам этого не позволяет — меняйте фреймворк или сеняйте архитектуру своего приложения так, чтобы он соответствовал возможностям фреймворка и не приводил к грязным хакам. Причём не только грязным, но и глобальным для всего приложения.
Рассматривалась ли возможность использования паттернов Стратегия или Обозреватель?
Нечто подобное Наблюдателю реализовано в 1с в 8.1
Недостатки таких решений это сложность понимания и дополнительный код. Который опять таки увеличивает сложность понимания. Поскольку у 1с свой язык и свой движок, то они могут скрыть реализацию таких вещей не запутывая кодера которому не нужен этот функционал. Но это тоже грязновато…

В моем же решении присутствует универсальность (переопределяй ЛЮБОЙ класс а не только те которые тебе разрешили и специально подготовили) и отсутствие любых изменений до того как кому-то понадобится внести изменение. Код прост для изучения, нет ничего лишнего и т.п.

Минус конечно в том, что этим решением могут пользоваться бездумно и там где без этого можно обойтись. Ну не всех проконтролируешь — предупреждения я пишу… :)

ПЫСЫ: Если коротко то использование Наблюдателя, Стратегии и прочих подобных шаблонов ведут к заметному повышению порога вхождения.
Лучше выше порог вхождения, чем засраная говнокодом система ;)
Ну это уже холивар.
Есть задача — простой движок с низким порогом вхождения и высокой гибкостью.
Решение проблемы того, что человек решает задачи не сообразно своей квалификации планируется делать организационно.
Ну, не знаю, не знаю. Стратегию начал использовать ещё до того как узнал что такое стратегия. А к событиям должны были привыкнуть ещё в других языках.

И, вообще, почему Вы считаете, что порог вхождения в Ваше ПО ниже при использовании нераспространённых хаков ниже нежели использование распространённых шаблонов?
Потому что «нераспространенные хаки» не нужно будет использовать тем кто будет только входить :)
Изменения редки. Очень редки, и писать их (я надеюсь) будут уже опытные разработчики. А вот все эти навороты мешать будут именно тем кто только начинает разбираться.

Главный принцип простоты — чем меньше ты знаешь о чем-то пока ты с этим не столкнулся, тем лучше.
Простите, а не проще ли было использовать Dependency Injection (Например Pimple)?
Опять таки — куча лишнего кода + более высокий порог вхождения.
Да и частный случай тоже туда же.
То бишь по Вашему действительно лучше регистрировать отдельный автозагрузчик для каждого класса, которой Вам захотелось переопределить?

Да и о каком высоком пороге вхождения Вы говорите?
Куда уж проще то?

<?php

require_once 'lib/Pimple.php';

$neededClass = 'PerfectSoul'; // Имя нужного класса можно разместить в конфиге или еще где

$container = new Pimple; // Создали контейнер

$container['soul'] = new $neededClass; // Присвоили ячейке контейнера 'soul' объект нужного класса

$container['soul']->oldMethod(); // PROFIT
Ну можно не регистрировать загрузчик а сделать просто прямой вызов. Или сделать один загрузчик на все изменения если у тебя в проекте их много. Не суть. Главное что все они в одном заметном месте.

Сложность в том, что все это надо будет делать В КАЖДОМ из уже более чем 70 классов которые имеют место сейчас… а это только начало разработки…
Захардкодить (а именно так называется то, что Вы предлагаете) имена классов в автозагрузчике(ах) ни как не гибкое решение (И понятности для новичков я в таком подходе тоже не наблюдаю, по мне так проще будет в конфиге эти классы задавать, и нагляднее и возможности сломать все к чертям особо нет). И это занятие не проще, чем переделать на DI, на мой взгляд.
Есть предложение к коментаторам обладающим некоторым свободным временем и умением связывать слова в предложения.
Здесь упомянуты несколько разных подходов к решению определенного класса задач. Может ли кто-нибудь написать топик, в котором решить одну и ту же задачу различными способами? Идеально из 2х начальных точек: «с нуля» и «из имеющейся системы».
Определённого? Кем? Где?
Своими словами: [локальное] изменение поведения существующего класса, используемого в неконтролируемых модулях, без внесения их [конкретных изменений] непосредственно в код этого класса.
Место действия: PHP5.3+ (5.4+).
Можете использовать собственную, быть может, более корректную и практичную формулировку.
Мне кажется, что Вы говорите о такой штуке как Mixins, для примера, в Yii это называется Behavior. Или я не так понял?
Почти. Я пытаюсь сказать о проблеме, которую они решают.
Да, я понял. Просто тут все могут разойтись во мнениях, какую именно технику использовать, и нет среди них лучшей, серебряной пули, так сказать :) Где-то проще будет использовать Mixins, где-то лучше подойдет Runkit.
Собственно поэтому я и предложил сделать сравнение. Runkit-стремная штука, судя по его багтрекеру.
Лучше расскажите о тех модулях, которые Вы встречали, с которыми вот такие вот проблемы.
Отсутствие вертолетного винта у гоночного болида не является его проблемой.
Ограниченность и сложность модернизации структуры большинства фреймворков это лишь особенность.
Хватит уже мыслить проблемами.
Раздражает эта ограниченная психология — будет проблема, будем решать. Создавать что-то новое, самому создавать ниши а не заполнять чужие — нет, это не для «наших людей».
> Отсутствие вертолетного винта у гоночного болида не является его проблемой.

Понятное дело, потому что гоночный болид называется гоночным болидом, а не конструктором или фреймворком. Если хотите делать гоночный болид, то делайте, никто ничего Вам не скажет. Но, если Вы сделав гоночный болид, назовёте его конструктором и предложите в качестве инструментов доработки сварочную горелку, зубило и молоток, сверло и токарный станок, то на Вас будут косо смотреть.

А Ваш подход «будет проблема, будем решать» очень распространён. Поставят шах — защитим короля. Стукнут машину — подумаем о страховке. Собьёт пьяный на дороге 7-х — будем думать над законом. Грянет гром — перекрестимся.
Ну так я вроде как и примерный список возможных задач накидал, и вроде как против проблем писал а не за ;)
Тогда ладно: ) Проблемы оставлять на потом не стоит, но и придумывать не надо.: )
Здесь было мельком затронуто три подхода (помимо моего) — хуки, события и мутации.
Умножить на новая/старая система это 6 примеров.
Довольно много.
Я на эту заметку часа полтора потратил.
Имея четкое понимание того о чем пишу.
Но было бы неплохо.....)

ПЫСЫ: Если вдруг кто-то решится на такой обзор, то просьба от меня сразу указывать степень удовлетовренности таких критериев:
1 — степень усложнения кода в чистой системе для ее подготовки к возможности переопределения (для меня важно что мой код чистый остался)
2 — универсальность (мне важно, что мое решение позволяет изменять любые классы а не только те которые было запланировано модифицировать заранее).
3 — «правильность» кода (для меня не так важно, но как вижу многим это очень важно).
Да, но «Это расширение PECL не поставляется вместе с PHP.».
А значит пока не станет частью ядра как SPL я его не использую.
Я и на SPL перешел только после того как 5.3 стал достаточно распространенным.
Раньше эммулировал эту функцию через издевательство над __автолоад
использование VPS или полноценного сервера решает эти архаичные проблемы.
Я в статье не нашёл упоминания о shared хостинге.
Может потому что его там не было? :)
Я опубликовал способ решения задачи.
Мало того я даже пометил его как "dirty hack" в названии заметки. Чтоб было меньше вопросов.
А дальше каждый решает сам что ему больше подходит: простое и универсальное «неправильное» решение или сложное решение которое работает не везде и может решить не все задачи, но при этом «правильное».
Спасибо. Не нашел ссылку туда на *.php.net.
Посмотрите на фреймворк Kohana, там Вашу проблему решили давно ;-)
Сформулируйте пожалуйста поконкретнее, в чем именно моя проблема и как они ее решили. Если сложно указать к какому из уже обсужденных решений оно относится, то просто ссылку дайте плиз, чтобы не рыться только для того чтобы убедиться что ничего нового там нет…

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

ПЫСЫ: Не сочтите за троллинг или самомнение, но уже надоели придуманные проблемы и повторы решений за пределами ТЗ.
ПЫПЫСЫ: Я пока вообще другое решение реализовал — расставил приоритеты загрузки модулей из разных папок. Теперь модули сайта стали приоритетнее чем стандартные модули и модули ядра, так что можно просто создать нужные классы там, и ничего настраивать не нужно будет. Не стал уже апать тему, чтобы опять свора «правильных архитекторов» не загрызла, а в качестве примера того как работает spl_autoload_register статья остается полезной. К тому же у этого решения все равно есть некоторые преимущества.
Sign up to leave a comment.

Articles