Скажем, у меня есть объект с атрибутом «тип». Там может быть более 20 возможных типов.
Теперь меня попросили реализовать что-то, что позволило бы изменить тип с A-> B, что является единственным вариантом использования.
Так я должен реализовать что-то, что позволяет произвольные изменения типа, если они являются допустимыми типами? Или я должен ТОЛЬКО позволить ему измениться с A-> B согласно требованию и отклонить любое другое изменение типа, такое как B-> A или A-> C?
Я вижу плюсы и минусы с обеих сторон, где общее решение будет означать меньше работы в случае появления аналогичного требования в будущем, но это также будет означать больше шансов ошибиться (хотя мы на 100% контролируем абонента в этом точка).
Конкретное решение менее подвержено ошибкам, но требует больше работы в будущем, если возникнет аналогичное требование.
Я продолжаю слышать, что хороший разработчик должен попытаться предвидеть изменения и спроектировать систему так, чтобы ее можно было легко расширять в будущем, что звучит как универсальное решение?
Редактировать:
Добавление большего количества деталей к моему не столь конкретному примеру: «универсальное» решение в этом случае требует меньше работы, чем «конкретное» решение, поскольку конкретное решение требует проверки как старого, так и нового типа, в то время как универсальное решение нужно только подтвердить новый тип.
источник
Ответы:
Мое эмпирическое правило:
Конечно, это руководство, а не жесткое и быстрое правило: реальный ответ - использовать свое лучшее суждение в каждом конкретном случае.
источник
thing1, thing2
, подумайте, может быть, используя массив. Вthing1, thing2, thing3
, почти наверняка использоватьthing[]
вместо этого массив». Это аналогичное правило для выбора между несколькими переменными или одним массивом.Я слышал этот аргумент несколько десятков раз, и, по моему опыту, он регулярно оказывается ошибочным. Если вы обобщите сейчас или позже, когда возникнет второе подобное требование, работа будет почти одинаковой. Поэтому нет абсолютно никакого смысла вкладывать дополнительные усилия в обобщение, если вы не знаете, что эти усилия когда-нибудь окупятся.
(Очевидно, что это не относится к случаям, когда более общее решение является менее сложным и требует меньше усилий, чем конкретное, но, по моему опыту, это редкие случаи. Такой вид сценария был впоследствии отредактирован в вопросе, и это не один мой ответ о).
Когда появляется «второй подобный случай», тогда пришло время задуматься об обобщении. Тогда будет гораздо проще правильно обобщить , потому что это второе требование дает вам сценарий, в котором вы можете проверить, правильно ли вы сделали общие выводы. При попытке обобщить только один случай, вы стреляете в темноте. Скорее всего, вы чрезмерно обобщаете определенные вещи, которые не нужно обобщать, и упускаете другие части, которые должны. И когда возникает второй случай, и вы понимаете, что обобщили неправильные вещи, у вас есть гораздо больше работы , чтобы исправить это.
Поэтому я рекомендую отложить любое искушение делать что-то «на всякий случай». Такой подход приведет к дополнительным усилиям по работе и обслуживанию, когда вы упустили возможность обобщить три, четыре или более раз, а затем у вас будет куча кода с похожим (таким повторяющимся) кодом для обслуживания.
источник
TL; DR: это зависит от того, что вы пытаетесь решить.
У меня был похожий разговор с моими Gramps об этом, когда мы говорили о том, как Func и Action в C # замечательны. My Gramps - очень старый программист таймера, он работал с исходными кодами с тех пор, как программное обеспечение запускалось на компьютерах, занимающих целую комнату.
Он несколько раз менял техников в своей жизни. Он написал код на C, COBOL, Pascal, BASIC, Fortran, Smalltalk, Java и в конце концов начал C # в качестве хобби. Я научился программировать с ним, сидя у него на коленях, пока я был просто придурком, вырезая мои первые строки кода на синем редакторе IBM SideKick. К 20 годам я уже потратил больше времени на программирование, чем на игры на улице.
Это немного моих воспоминаний, так что извините, если я не совсем практичен, пересказывая их. Я немного люблю эти моменты.
Вот что он сказал мне:
«Должны ли мы пойти на обобщение проблемы или решить ее в конкретной области, вы спрашиваете? Ну, это ... вопрос».
Грэмпс сделал паузу, чтобы немного подумать об этом, фиксируя положение очков на лице. Он играл в матч-3 на своем компьютере, слушая LP Deep Purple на своей старой звуковой системе.
«Ну, это будет зависеть от того, какую проблему вы пытаетесь решить», - сказал он мне. «Соблазнительно полагать, что существует единственное, святое решение для всех вариантов дизайна, но его нет. Видите ли, архитектура программного обеспечения подобна сыру».
"... Сыр, Gramps?"
«Не имеет значения, что вы думаете о своем любимом, всегда найдется кто-то, кто думает, что это вонючий».
Я моргнул в замешательстве, но прежде чем я успел что-то сказать, продолжал Грэмпс.
«Когда вы строите машину, как вы выбираете материал для детали?»
«Я ... я полагаю, это зависит от затрат и того, что часть должна делать, я полагаю».
«Это зависит от проблемы, которую пытается решить деталь. Вы не будете делать шину из стали или ветровое стекло из кожи. Вы выбираете материал, который наилучшим образом решает проблему, с которой вы столкнулись. универсальное решение? Или конкретное? К какой проблеме, к какому варианту использования? Следует ли использовать полнофункциональный подход, чтобы обеспечить максимальную гибкость кода, который будет использоваться только один раз? Следует ли писать очень специализированный, хрупкий код для часть вашей системы, которая будет видеть множество вариантов использования и, возможно, множество изменений? Варианты дизайна, подобные этим, подобны материалам, которые вы выбираете для детали в автомобиле, или форме кирпича Lego, который вы выбираете, чтобы построить маленький дом. Какой кирпич Lego самый лучший?
Пожилой программист потянулся к маленькой модели поезда Lego, которую у него на столе, прежде чем продолжить.
«Вы можете ответить на этот вопрос, только если знаете, для чего вам нужен этот кирпич. Как, черт возьми, вы узнаете, что конкретное решение лучше общего, или наоборот, если вы даже не знаете, с какой проблемой вы работаете? пытаясь решить? Вы не можете увидеть прошлый выбор, который вы не понимаете ".
"... Вы только что процитировали Матрицу? "
"Какая?"
«Ничего, продолжай».
«Ну, предположим, вы пытаетесь что-то встроить в Национальную систему счетов. Вы знаете, как этот адский API и его XML-файл из тридцати тысяч строк выглядят изнутри. Как бы выглядело« универсальное »решение для создания этого файла? как? Файл полон необязательных параметров, полных случаев, которые должны использовать только очень специфические отрасли бизнеса. В большинстве случаев их можно спокойно игнорировать. Вам не нужно создавать общую систему счетов, если это единственное, что у вас есть » Вы когда-нибудь будете продавать обувь. Просто создайте систему продажи обуви и сделайте ее самой лучшей в мире системой счетов-фактур по продаже обуви. Теперь, если вам нужно было создать систему счетов-фактур для любого типа клиента, для более широкого применения - быть перепроданным как независимая, общая система продаж,например - сейчас интересно реализовать те варианты, которые используются только для газа, еды или алкоголя.Теперь это возможные варианты использования. Раньше они были просто гипотетическими. Не используйте случаи, и вы не хотите реализовывать Не используйте случаи. Не используйте это младший брат Не нужно ".
Грэмпс вернул лего-поезд на свое место и вернулся к своей игре «три в ряд».
«Таким образом, чтобы иметь возможность выбрать общее или конкретное решение для данной проблемы, вам сначала нужно понять, что, черт возьми, эта проблема. В противном случае вы просто угадываете, и угадываете работу менеджеров, а не программистов. все в ЭТОМ, это зависит ".
Итак, вот оно. "По-разному". Это, наверное, самое мощное выражение из двух слов, когда мы думаем о разработке программного обеспечения.
источник
Прежде всего, вы должны попытаться предвидеть вероятность того, что такое изменение произойдет, а не просто отдаленную возможность где-нибудь в будущем.
Если нет, то обычно лучше перейти к простому решению сейчас и расширить его позже. Вполне возможно, что у вас будет гораздо более четкая картина того, что нужно тогда.
источник
Если вы работаете в новой для вас области, тогда определенно должно быть применено Правило трех , упомянутое Дэниелом Приденом. В конце концов, как вы должны создавать полезные абстракции, если вы новичок в этой области? Люди часто уверены в своей способности ловить абстракции, хотя это редко так. По моему опыту, преждевременная абстракция - не меньшее зло, чем дублирование кода. Неправильные абстракции действительно больно понимать. Иногда даже более болезненно рефакторинг.
Есть книга, посвященная моей точке зрения о неизвестной области, в которой работает разработчик. Она состоит из определенных доменов с извлеченными полезными абстракциями.
источник
Учитывая природу вашего вопроса, предполагая, что я правильно его понял, я на самом деле вижу в этом вопрос проектирования, касающийся особенностей центральной системы, а не вопроса об общих или конкретных решениях.
А когда речь идет о функциях и возможностях центральной системы, наиболее надежными являются те, которых нет. Это оправдывает ошибку на стороне минимализма, особенно учитывая, что обычно проще добавить функциональность централизованно, долго желательно, чем удалить проблемную функциональность, долго нежелательную, с многочисленными зависимостями, потому что это значительно усложняет работу с системой. чем нужно, поднимая бесконечные вопросы дизайна с каждой новой функцией.
На самом деле, не имея четкого представления о том, будет ли это необходимо часто в будущем, я бы постарался не рассматривать это как замену типа из А в В, если это возможно, а вместо этого просто искать его как способ преобразования состояния А. Например, установите некоторые поля в A так, чтобы он преобразовывался и отображался пользователю как B, фактически не переходя в другой «тип» вещей - вы могли бы потенциально сделать A-хранилище B частным образом, используя функции композиции и вызова в B, когда состояние A устанавливается, чтобы указать, что он должен имитировать B, чтобы упростить реализацию, если это возможно. Это должно быть очень простым и минимально инвазивным решением.
Так или иначе, повторяя многие другие, я бы посоветовал ошибиться в том, чтобы избежать общих решений в этом случае, но тем более, потому что я предполагаю, что это связано с добавлением очень смелых возможностей в центральную систему, и там я бы предлагаю ошибиться на стороне опущения, особенно сейчас.
источник
Трудно дать общий ответ на эту конкретную проблему ;-)
Чем оно более общее, тем больше времени вы получите для будущих изменений. Например, по этой причине многие игровые программы используют шаблон компонентов сущностей вместо создания очень сложной, но жесткой системы типов персонажей и объектов в игре.
С другой стороны, создание чего-то общего требует предварительных вложений времени и усилий в дизайн, что намного выше, чем для чего-то очень специфического. Это несет в себе риск чрезмерного проектирования и даже потерянных потенциальных требований в будущем.
Всегда стоит посмотреть, есть ли естественное обобщение, которое даст вам прорыв. Однако, в конце концов, это вопрос баланса между усилиями, которые вы можете потратить сейчас, и усилиями, которые вам могут понадобиться в будущем.
источник
Гибридный. Это не должно быть или / или вопросом. Вы можете разработать API для общих преобразований типов, в то же время реализуя только конкретное преобразование, которое вам нужно прямо сейчас. (Просто убедитесь, что если кто-то вызывает ваш общий API с неподдерживаемым преобразованием, он завершается с ошибкой «не поддерживается».)
Тестирование. Для преобразования A-> B мне нужно написать один (или небольшое количество) тестов. Для общего преобразования x-> y мне, возможно, придется написать целую матрицу тестов. Это существенно больше работы, даже если все преобразования имеют одну общую реализацию.
Если, с другой стороны, найдется универсальный способ проверки всех возможных конверсий, тогда работы не так много, и я мог бы склониться к более быстрому поиску универсального решения.
Связь. Для преобразования из A в B может потребоваться информация о реализации A и B (тесная связь). Если A и B все еще развиваются, это означает, что мне, возможно, придется пересматривать конвертер (и его тесты), который отстой, но, по крайней мере, он ограничен A и B.
Если бы я использовал универсальное решение, которому нужен доступ к деталям всех типов, то даже по мере развития C и D мне, возможно, придется продолжать настраивать универсальный конвертер (и кучу тестов), что может замедлить меня даже хотя никто еще не нужно преобразовать в C или D .
Если и общие, и конкретные преобразования могут быть реализованы таким образом, что они слабо связаны с деталями типов, я бы не стал беспокоиться об этом. Если один из них может быть выполнен слабосвязанным способом, а другой требует тесной связи, то это сильный аргумент в пользу слабосвязанного подхода прямо из ворот.
источник
Это не ответный вопрос.
Лучшее, что вы можете разумно получить, - это несколько эвристических методов, чтобы решить, насколько общим или конкретным является принятие данного решения. Работая через что-то вроде процесса, описанного ниже, обычно приближение первого порядка является правильным (или достаточно хорошим). Когда это не так, причина, вероятно, слишком специфична для предметной области, чтобы ее можно было подробно рассмотреть здесь.
Аппроксимация первого порядка: обычное правило трех YAGNI, описанное Дэниелом Приденом, Доком Брауном и соавт .
Это обычно полезная эвристика, потому что, вероятно, лучшее, что вы можете сделать, не зависит от домена и других переменных.
Итак, исходное предположение таково: мы делаем самую конкретную вещь.
Аппроксимация второго порядка: на основании ваших экспертных знаний в области решения , вы говорите
поэтому мы могли бы интерпретировать YAGNI как рекомендацию избегать ненужной работы , а не избегать ненужной общности. Таким образом, мы могли бы изменить наше первоначальное предположение и вместо этого сделать самое простое .
Однако если ваши знания в области решения указывают на то, что простейшее решение может привести к появлению большого количества ошибок или затруднению адекватного тестирования или возникновению любой другой проблемы, то упрощение кода не обязательно является достаточной причиной для изменения нашего оригинальный выбор.
Аппроксимация третьего порядка: предполагает ли ваше знание предметной области, что простейшее решение действительно правильное, или вы допускаете множество переходов, которые, как вы знаете, являются бессмысленными или неправильными?
Если простое, но общее решение выглядит проблематичным или вы не уверены в своей способности оценить эти риски, лучше выполнить дополнительную работу и придерживаться первоначального предположения.
Приближение четвертого порядка: ваши знания о поведении клиента, или как эта функция связана с другими, или приоритеты управления проектом, или ... какие-либо другие, не строго технические соображения, изменяют ваше текущее рабочее решение?
источник
Это не простой вопрос, чтобы ответить простым ответом. Многие ответы дали эвристику, построенную вокруг правила 3 или чего-то подобного. Выйти за пределы таких правил сложно.
Чтобы действительно действительно ответить на ваш вопрос, вы должны учитывать, что ваша работа, скорее всего, не реализует то, что меняет A-> B. Если вы подрядчик, возможно, это требование, но если вы работник, вас нанимают для выполнения множества мелких задач для компании. Изменение A-> B - только одна из тех задач. Ваша компания будет заботиться о том, насколько хорошо могут быть внесены будущие изменения, даже если это не указано в запросе. Чтобы найти «TheBestImplementation (tm)», вы должны взглянуть на более широкую картину того, что вас действительно просят сделать, а затем использовать это, чтобы интерпретировать небольшой запрос, который вам дали для изменения A-> B.
Если вы начинающий программист низкого уровня, только что закончивший колледж, часто рекомендуется делать именно то, что вам сказали. Если вы были наняты в качестве архитектора программного обеспечения с 15-летним опытом работы, обычно рекомендуется подумать о вещах, представляющих большую картину. Любая реальная работа упадет где-то между «делай то, что является узкой задачей» и «думай о картине». Вы почувствуете, как ваша работа вписывается в этот спектр, если вы достаточно поговорите с людьми и сделаете для них достаточно работы.
Я могу привести несколько конкретных примеров, когда ваш вопрос имеет четкий ответ в зависимости от контекста. Рассмотрим случай, когда вы пишете программное обеспечение, критически важное для безопасности. Это означает, что у вас есть команда тестировщиков, чтобы убедиться, что продукт работает, как обещано. Некоторые из этих групп тестирования обязаны тестировать каждый возможный путь через код. Если вы поговорите с ними, вы можете обнаружить, что, обобщив поведение, вы добавите $ 30 000 к их затратам на тестирование, потому что им придется тестировать все эти дополнительные пути. В этом случае не добавляйте обобщенную функциональность, даже если из-за этого вам придется дублировать работу 7 или 8 раз. Сэкономьте деньги компании и сделайте именно то, что указано в заявке.
С другой стороны, учтите, что вы создаете API, позволяющий клиентам получать доступ к данным в программе базы данных, которую создает ваша компания. Клиент просит разрешить изменения A-> B. API, как правило, имеют аспект золотых наручников: когда вы добавляете функциональность в API, вы, как правило, не должны удалять эту функциональность (до следующего номера основной версии). Многие из ваших клиентов, возможно, не захотят платить за обновление до следующей основной версии, поэтому вы можете надолго остаться без решения, которое вы выберете. В этом случае я настоятельно рекомендую создать универсальное решение с самого начала. Вы действительно не хотите разрабатывать плохой API, полный одноразового поведения.
источник
Хм ... не так уж много контекста для ответа ... повторяя предыдущие ответы: "Это зависит".
В некотором смысле, вы должны прибегнуть к своему опыту. Если не ваш, то кто-то постарше в домене. Вы могли бы поспорить о том, что утверждают критерии принятия. Если это что-то вроде «пользователь должен иметь возможность изменить тип с« A »на« B »« против », то пользователь должен иметь возможность изменить тип с его текущего значения на любое допустимое альтернативное значение».
Часто критерии приемлемости подлежат интерпретации, но хороший персонал по обеспечению качества может написать критерии, соответствующие поставленной задаче, сводя к минимуму необходимую интерпретацию.
Существуют ли ограничения домена, которые не позволяют изменить значение с «A» на «C» или любой другой параметр, а только с «A» на «B»? Или это просто узкоспециализированное требование, которое не является «дальновидным»?
Если бы общий случай был более сложным, я бы спросил перед началом работы, но в вашем случае, если бы я мог «предсказать», что другие «типовые» запросы на изменение будут поступать в будущем, у меня возникнет соблазн: а) напишите что-нибудь повторно используемое для общего случая и б) оберните это в условное выражение, которое пока допускает только A -> B.
Достаточно просто проверить текущий случай в автоматическом тестировании и достаточно легко открыть другие варианты позже, если / когда возникнут разные варианты использования.
источник
Для меня руководство, которое я установил некоторое время назад: «Для гипотетических требований пишите только гипотетический код». То есть - если вы ожидаете дополнительных требований, вам следует немного подумать о том, как их реализовать, и структурировать свой текущий код так, чтобы он не блокировал таким образом.
Но не пишите настоящий код для них сейчас - просто подумайте о том, что вы будете делать. В противном случае вы, как правило, усложняете задачу и, вероятно, будете раздражены позже, когда появятся реальные требования, отличающиеся от ожидаемых.
Ваш пример: если вы используете все методы метода convert, находящиеся под вашим контролем, вы можете просто вызвать сейчас метод convertAToB и планировать использовать рефакторинг «метод переименования» в IDE, чтобы переименовать его, если позже вам понадобится более общая функциональность. Однако, если метод преобразования является частью общедоступного API, это может быть совсем по-другому: его специфика заблокирует обобщение позже, поскольку в этом случае трудно переименовать вещи.
источник
В принципе да. Но это не обязательно приводит к общим решениям.
Насколько мне известно, в разработке программного обеспечения есть два типа тем, в которых вы должны предвидеть будущие изменения:
Первый случай решается путем наблюдения за вашей сплоченностью / связью, внедрением зависимости или чем-то еще. Второй случай находится на более абстрактном уровне, например, выбор большой сервисно-ориентированной архитектуры вместо большого монолотического куска кода для большого приложения.
В вашем случае вы запрашиваете конкретное решение для конкретной проблемы, которое никак не повлияет на будущее. В этом случае YAGNI и DRY являются хорошими девизами для:
В сочетании с другими современными практиками (такими как хорошее тестовое покрытие для обеспечения возможности безопасного рефакторинга) это означает, что в итоге вы получите быстро написанный, скудный, средний код, который растет по мере необходимости.
Нет, похоже, у вас должны быть среды программирования, языки и инструменты, которые позволяют легко и весело проводить рефакторинг, когда вам это нужно. Общие решения не обеспечивают этого; они отделяют приложение от фактического домена.
Взгляните на современные ORM или MVC-фреймворки, например Ruby on Rails; на уровне приложения все внимание сосредоточено на выполнении нестандартной работы. Сами библиотеки rails, очевидно, почти на 100% универсальны, но код домена (о чем ваш вопрос) должен выполнять минимальные махинации в этом аспекте.
источник
Другой способ осмыслить проблему - рассмотреть, что имеет смысл.
Например, я разрабатывал приложение, в котором были разделы, которые делали почти то же самое, но имели противоречивые правила доступа. Так как не было причин оставлять их разными, когда я реорганизовал этот раздел, я заставил их всех делать разрешения одинаково. Благодаря этому общий код стал меньше, проще и интерфейс стал более согласованным.
Когда руководство решило предоставить другим людям доступ к какой-либо функции, мы смогли сделать это, просто изменив флаг.
Очевидно, имеет смысл сделать конкретное преобразование типов. Имеет ли смысл также делать дополнительные преобразования типов?
Имейте в виду, что если общее решение быстрее реализовать, то и конкретный случай также прост, просто убедитесь, что это единственное преобразование типов, которое вы разрешаете.
Если заявка находится в строго регламентированной области (медицинская или финансовая), постарайтесь привлечь больше людей к вашему дизайну.
источник