Я посмотрел на некоторые ответы и искал в Google, но я не смог найти ничего полезного (то есть, это не будет иметь неловких побочных эффектов).
Моя абстрактная проблема в том, что у меня есть объект и мне нужно выполнить длинную последовательность операций над ним; Я думаю об этом как о сборочной линии, как о сборке машины.
Я считаю, что эти объекты будут называться объектами метода .
Так что в этом примере в какой-то момент у меня будет CarWithoutUpholstery, на котором мне нужно будет запустить installBackSeat, installFrontSeat, installWoodenInserts (операции не мешают друг другу и могут даже выполняться параллельно). Эти операции выполняются CarWithoutUpholstery.worker () и дают новый объект, который будет CarWithUpholstery, для которого я затем запустил бы, возможно, cleanInsides (), verifyNoUpholsteryDefects () и так далее.
Операции в одной фазе уже независимы, т. Е. Я уже имею дело с их подмножеством, которое может быть выполнено в любом порядке (переднее и заднее сидения могут быть установлены в любом порядке).
Моя логика в настоящее время использует Reflection для простоты реализации.
То есть, когда у меня есть CarWithoutUpholstery, объект проверяет себя на наличие методов с именем executeSomething (). В этот момент он выполняет все эти методы:
myObject.perform001SomeOperation();
myObject.perform002SomeOtherOperation();
...
при проверке ошибок и прочего. Хотя порядок операций не важен, я назначил лексикографический порядок на случай, если когда-нибудь обнаружу, что какой-то порядок важен в конце концов. Это противоречит YAGNI , но стоит очень мало - простая сортировка () - и это может сэкономить массовое переименование метода (или введение какого-либо другого метода выполнения тестов, например, массива методов).
Другой пример
Скажем, вместо того, чтобы строить машину, я должен составить отчет Секретной полиции о ком-то и передать его моему Злому Повелителю . Мой последний объект будет ReadyReport. Чтобы построить его, я начинаю со сбора основной информации (имя, фамилия, супруг ...). Это моя фаза A. В зависимости от того, есть ли супруг или нет, мне, возможно, придется перейти к фазам B1 или B2 и собрать данные о сексуальности для одного или двух человек. Это сделано из нескольких разных запросов к различным Злым Миньонам, контролирующим ночную жизнь, уличные камеры, квитанции о продажах в секс-шопах и тому подобное. И так далее.
Если у жертвы нет семьи, я даже не войду в фазу GetInformationAboutFamily, но если я это сделаю, то не имеет значения, нацелена ли я сначала на отца, на мать или на братьев и сестер (если есть). Но я не могу этого сделать, если я не выполнил FamilyStatusCheck, который, следовательно, относится к более раннему этапу.
Все работает чудесно ...
- если мне нужна дополнительная операция, мне нужно только добавить приватный метод,
- если операция является общей для нескольких этапов, я могу унаследовать ее от суперкласса,
- операции просты и автономны. Ни одно значение из одной операции не всегда требует какой - либо из других (операций , которые действительно выполняются в другой фазе),
- Объекты, находящиеся внизу линии, не должны выполнять много тестов, поскольку они даже не могли бы существовать, если их создатели не проверили эти условия в первую очередь. Т.е. при размещении вставок в приборной панели, очистке приборной панели и проверке приборной панели мне не нужно проверять, что приборная панель действительно есть .
- это позволяет легко тестировать. Я могу легко смоделировать частичный объект и запустить любой метод для него, и все операции являются детерминированными черными ящиками.
...но...
Проблема возникла, когда я добавил одну последнюю операцию в один из объектов моего метода, в результате чего общий модуль превысил обязательный индекс сложности («меньше, чем N частных методов»).
Я уже поднял этот вопрос наверх и предположил, что в этом случае множество частных методов не является признаком катастрофы. Сложность это есть, но она есть , потому что операция является сложной, и на самом деле это не все , что комплекс - это просто долго .
Используя пример Злого Повелителя, моя проблема в том, что Злой Повелитель (он же Тот, кому не будет отказано ), запросив всю диетическую информацию, мои диетические миньоны сказали мне, что мне нужно запрашивать рестораны, мини-кухни, уличных торговцев, нелицензированных уличных торговцев, теплицу владельцы и т. д., и Злой (подчиненный) Повелитель - известный также как Тот, Кто также не откажется - жалующийся на то, что я выполняю слишком много запросов на этапе GetDietaryInformation.
Примечание . Мне известно, что с нескольких точек зрения это вообще не проблема (игнорирование возможных проблем с производительностью и т. Д.). Все, что происходит, заключается в том, что конкретный показатель недоволен, и этому есть основания.
Что я думаю, я мог сделать
Помимо первого, все эти варианты выполнимы и, я думаю, оправданны.
- Я проверил, что могу быть хитрым и объявить половину моих методов
protected
. Но я бы использовал слабость в процедуре тестирования, и кроме оправдания себя, когда меня поймали, мне это не нравится. Кроме того, это временная мера. Что если число необходимых операций удваивается? Вряд ли, но что тогда? - Я могу произвольно разделить эту фазу на AnnealedObjectAlpha, AnnealedObjectBravo и AnnealedObjectCharlie, и иметь одну треть операций, выполняемых на каждом этапе. У меня сложилось впечатление, что это на самом деле добавляет сложности (N-1 больше классов), без каких-либо преимуществ, кроме прохождения теста. Конечно, я могу утверждать, что CarWithFrontSeatsInstalled и CarWithAllSeatsInstalled являются логически последовательными этапами. Риск того, что метод Браво позже понадобится Альфе, невелик и даже меньше, если я буду хорошо его играть. Но все равно.
- Я могу объединить различные операции, удаленно похожие, в одну.
performAllSeatsInstallation()
, Это только временная мера, и она увеличивает сложность одной операции. Если мне когда-нибудь понадобится выполнить операции A и B в другом порядке, и я упаковал их в E = (A + C) и F (B + D), мне придется разложить E и F и перемешать код вокруг , - Я могу использовать массив лямбда-функций и аккуратно обходить проверку, но я нахожу это неуклюжим. Однако это пока лучшая альтернатива. Это избавило бы от размышлений. У меня есть две проблемы: меня, вероятно, попросят переписать все объекты методов, не только гипотетические
CarWithEngineInstalled
, и хотя это будет очень хорошей защитой работы, на самом деле это не так уж и привлекательно; и что у проверки покрытия кода есть проблемы с лямбдами (которые разрешимы, но все же ).
Так...
- Какой, ты думаешь, мой лучший вариант?
- Есть ли лучший способ, который я не рассмотрел? ( возможно, мне лучше прийти и спросить, что это? )
- Неужели этот дизайн безнадежно испорчен, и мне лучше признать поражение и рвоту - эту архитектуру вообще? Не хорошо для моей карьеры, но будет ли написание плохо спроектированного кода лучше в долгосрочной перспективе?
- Является ли мой текущий выбор «Единственно верным путем», и мне нужно бороться, чтобы установить метрики лучшего качества (и / или контрольно-измерительные приборы)? Для этого последнего варианта мне понадобятся ссылки ... Я не могу просто махнуть рукой на @PHB, бормоча. Это не те показатели, которые вы ищете . Независимо от того, сколько я хотел бы иметь возможность