Я потратил много времени на чтение разных книг о «хорошем дизайне», «шаблонах проектирования» и т. Д. Я большой поклонник подхода SOLID, и каждый раз, когда мне нужно написать простой кусок кода, я думаю о будущее. Таким образом, если для реализации новой функции или исправления ошибки требуется просто добавить три строки кода, например:
if(xxx) {
doSomething();
}
Это не значит, что я сделаю это так. Если я чувствую, что этот кусок кода может стать больше в ближайшем будущем, я подумаю о добавлении абстракций, перемещении этой функции в другое место и так далее. Цель, которую я преследую, - сохранить среднюю сложность такой же, какой она была до моих изменений.
Я считаю, что с точки зрения кода, это неплохая идея - мой код никогда не бывает достаточно длинным, и довольно легко понять значения для различных сущностей, таких как классы, методы и отношения между классами и объектами.
Проблема в том, что это занимает слишком много времени, и я часто чувствую, что было бы лучше, если бы я просто реализовал эту функцию «как есть». Это просто «три строки кода» против «нового интерфейса + два класса для реализации этого интерфейса».
С точки зрения продукта (когда мы говорим о результате ), вещи, которые я делаю, совершенно бессмысленны. Я знаю, что если мы собираемся работать над следующей версией, хороший код - это действительно здорово. Но с другой стороны, время, которое вы потратили на то, чтобы сделать ваш код «хорошим», могло быть потрачено на реализацию нескольких полезных функций.
Я часто чувствую себя очень неудовлетворенным своими результатами - хороший код, который может выполнять только A, хуже, чем плохой код, который может выполнять A, B, C и D.
Может ли такой подход привести к положительной чистой выгоде для программного проекта или это пустая трата времени?
источник
Ответы:
Это пахнет для меня как умозрительная общность . Не зная (или, по крайней мере, не будучи достаточно уверенным), что вашим клиентам понадобятся функции B, C и D, вы просто излишне усложняете свой дизайн. Более сложный код сложнее понять и поддерживать в долгосрочной перспективе. Дополнительная сложность оправдана только полезными дополнительными функциями. Но мы, как правило, очень плохо предсказываем будущее. Большинство функций, которые, по нашему мнению, могут понадобиться в будущем, никогда не потребуются в реальной жизни.
Так:
Хороший код, который может выполнять только A (но он делает это просто и аккуратно), ЛУЧШЕ, чем плохой код, который может выполнять A, B, C, D (некоторые из которых могут понадобиться в будущем).
источник
Время анекдота:
У меня работало два разработчика, которые склонялись к чрезмерному проектированию таким образом.
Для одного из них это практически остановило его производительность, особенно при запуске нового проекта. Особенно если проект по своей природе был довольно простым. В конечном счете, именно то программное обеспечение, которое работает сейчас, - это то, что нам нужно. Это стало так плохо, что мне пришлось его отпустить.
Другой разработчик, который был склонен к чрезмерному проектированию, воспользовался этим, будучи чрезвычайно продуктивным. Таким образом, несмотря на весь посторонний код, он все равно доставил быстрее, чем большинство. Однако теперь, когда он ушел, меня часто раздражает количество дополнительной работы, необходимой для добавления функциональности, так как вам приходится изменять (совершенно ненужные) слои абстракции и т. Д.
Так что мораль в том, что чрезмерная инженерия съедает дополнительное время, которое можно было бы потратить на выполнение полезных дел. И не только ваше собственное время, но и тех, кто должен работать с вашим кодом.
Так что не надо.
Вы хотите, чтобы ваш код был максимально простым (и не более простым). Обработка 'maybes' не упрощает ситуацию, если вы догадаетесь неправильно, вы сделаете код более сложным без реальной выгоды, чтобы показать это.
источник
Принципы SOLID и KISS / YAGNI являются почти полярными противоположностями. Кто-то скажет вам, что если doSomething () не может быть оправдан как неотъемлемая часть работы, которую выполняет класс, который его вызывает, он должен находиться в другом классе, который слабо связан и внедрен. Другой скажет вам, что если это то единственное место, где используется doSomething, то даже извлечение его в метод может быть излишним.
Это то, что делает хороших программистов на вес золота. «Правильная» структура является индивидуальной для каждого случая и требует знания текущей кодовой базы, будущего пути программы и потребностей бизнеса, стоящего за этой программой.
Мне нравится следовать этой простой трехэтапной методологии.
По сути, именно так вы смешиваете KISS с SOLID. Когда вы впервые напишите строку кода, для всех вы знаете, это будет одноразовым; это просто должно работать, и никого не волнует, как, так что не увлекайтесь. Во второй раз, когда вы помещаете курсор в эту строку кода, вы опровергли свою первоначальную гипотезу; при повторном рассмотрении этого кода вы, вероятно, расширяете его или подключаетесь к нему откуда-то еще. Теперь вы должны немного почистить его; извлекать методы для часто используемых лакомых кусочков, уменьшать или исключать кодирование копировать / вставлять, добавлять несколько комментариев и т. д. и т. д. В третий раз, когда вы вернетесь к этому коду, теперь это основное пересечение путей выполнения вашей программы. Теперь вы должны рассматривать это как основную часть вашей программы и применять правила SOLID, где это возможно.
Пример: вам нужно написать простую строку текста в консоли. В первый раз, когда это происходит, Console.WriteLine () просто отлично. Затем вы возвращаетесь к этому коду после того, как новые требования также диктуют запись той же строки в выходной журнал. В этом простом примере может не быть много повторяющегося кода «копировать / вставить» (или, может быть, есть), но вы все равно можете выполнить некоторую базовую очистку, возможно, извлечь метод или два, чтобы избежать встраивания операций ввода-вывода в более глубокую бизнес-логику , Затем вы возвращаетесь, когда клиент хочет получить тот же текстовый вывод в именованный канал для сервера мониторинга. Теперь, эта процедура вывода довольно важна; Вы транслируете один и тот же текст тремя способами. Это учебный пример алгоритма, который выиграл бы от составного паттерна; разработать простой интерфейс IWriter с методом Write (), Реализуйте этот интерфейс для создания классов ConsoleWriter, FileWriter и NamedPipeWriter и еще раз для создания составного класса «MultiWriter», затем представьте зависимость IWriter от вашего класса, настройте составной состав MultiWriter с тремя фактическими записывающими устройствами и подключите его. Теперь это довольно твердо; с этого момента, когда требования диктуют, что вывод должен идти куда-то новым, вы просто создаете новый IWriter и подключаете его к MultiWriter, не нужно трогать какой-либо существующий рабочий код.
источник
1) Иметь код, который делает только то, что должен делать.
2) Если вы планируете использовать код A, B, C и D, клиент в конечном итоге попросит вас указать E.
Ваш код должен делать то, что должен делать, вы не должны сейчас думать о будущих реализациях, потому что вы никогда не прекратите непрерывно менять свой код, и что более важно, вы будете чрезмерно проектировать свой код. Вы должны провести рефакторинг своего кода, как только почувствуете, что он нужен, из-за имеющихся возможностей, а не изо всех сил, чтобы подготовить его к тому, чего он еще не собирается делать, если, конечно, вы не планировали его как часть архитектуры проекта.
Я предлагаю вам прочитать хорошую книгу: Прагматичный программист . Это откроет ваш разум и научит вас, что вы должны делать, а что нет.
Кроме того, Code Complete - это отличный ресурс, полный полезной информации, которую должен прочитать каждый разработчик (не только программист).
источник
Может быть, здесь проблема.
На ранних этапах вы не представляете, каким будет конечный продукт. Или, если у вас есть, это неправильно. Точно. Это как 14-летний мальчик, который несколько дней назад спросил на Programmers.SE, должен ли он для своей будущей карьеры выбирать между веб-приложениями, и я не помню, что еще: довольно очевидно, что через несколько лет вещи он вроде изменится, его будут интересовать другие домены и т. д.
Если для того, чтобы написать три строки кода, вы создаете новый интерфейс и два класса, вы слишком перегружены. Вы получите код, который будет трудно поддерживать и трудно читать , просто потому, что для каждой полезной строки кода у вас есть две строки кода, которые вам не нужны. Не считая XML документации, юнит-тестов и т. Д.
Подумайте об этом: если я хочу знать, как функция реализована в вашем коде, мне было бы легче прочитать двадцать строк кода, или было бы быстрее и проще открывать десятки полупустых классов и интерфейсы, чтобы выяснить, какой из них использует какой, как они связаны и т. д.?
Помните: большая кодовая база означает больше обслуживания. Не пишите больше кода, когда вы можете избежать этого.
Ваш подход также вреден для других сторон:
Если вам нужно удалить функцию, не проще ли выяснить, где используется конкретный метод из двадцати строк, чем тратить свое время на то, чтобы понять зависимости между десятками классов?
Разве при отладке не проще иметь трассировку небольшого стека или вы предпочитаете читать десятки строк, чтобы выяснить, что вызывается чем?
В заключение, это похоже на преждевременную оптимизацию . Вы пытаетесь решить проблему, даже не будучи уверенным, существует ли проблема в первую очередь и где она есть. Работая над версией 1 вашего продукта, реализуйте функции, которые вам необходимо реализовать прямо сейчас; не думайте о том, что вы ожидаете реализовать через два года в версии 14.
источник
Написание большого количества кода, который (вероятно) никогда не будет использоваться, - очень хороший способ получить P45 . У вас нет хрустального шара и вы не знаете, в каком направлении будет развиваться разработка, поэтому тратить время на эти функции просто стоит денег без возврата.
источник
Попытка предсказать, что может понадобиться из кода в будущем, часто оказывается ненужной чрезмерной инженерией (привычка, которую я сейчас пытаюсь избавить). Я бы сказал, просто сделать три строки. Когда возникнет необходимость (и не раньше), проведите рефакторинг. Таким образом, ваш код всегда делает то, что ему нужно, не будучи слишком сложным и развивает хорошую структуру естественным путем за счет рефакторинга.
источник
Я часто говорю, что кодирование - это как светлая сторона / темная сторона Силы - «светлая» сторона требует больше усилий, но дает лучшие результаты. «Темная» сторона - быстрая и простая, она сразу дает большие преимущества, но развращает вас в будущем. Как только вы пойдете по темному пути, он навсегда будет доминировать в вашей судьбе.
Я сталкиваюсь с этим все время, на каждой работе, которую я когда-либо имел, это как проклятие, от которого я не могу убежать. Культура компании - это всегда путь темной стороны, и быстрые взломы / исправления, чтобы выдвинуть новые функции, и мои просьбы и вопли о рефакторинге и написании кода должным образом игнорируются, если это не приводит к моему увольнению за " пытаясь что-то изменить »(без шуток, это случалось несколько раз, потому что я хотел представить шаблоны проектирования и отойти от быстрых взломов).
Грустная правда в том, что часто путь тупой / темной стороны - это путь, которым вы должны идти, вы просто должны быть уверены, что шагните легко. Я медленно и грустно осознавал, что программисты, которые понимают правильный способ написания программного обеспечения, то есть следуют SOLID, используют шаблоны проектирования, подчиняются SoC и т. Д., Гораздо реже, чем невежественные хаки, которые напишут
if
оператор для исправления ошибки, и когда возникает больше ошибок, просто добавьте к этому утверждению вместо того, чтобы думать «может быть, есть лучший способ сделать это» и рефакторинг кода, чтобы он был должным образом расширяемым.источник
if
намного проще в обслуживании, чемIAbstractBugFixer
изIAbstractBugFixerFactory
. Когда и, ЕСЛИ, вы добавите секундуif
, тогда пришло время провести рефакторинг. Шаблоны проектирования и SOLID очень важны на этапе архитектуры, но не тогда, когда продукт уже запущен и написан в едином стиле, с которым согласились все члены команды.Знать, что может случиться (будущее), НИКОГДА не плохо. Размышление о том, что может быть лучшим способом сделать что-то, является частью того, что делает вас хорошими на работе. Сложнее всего определить, оправдано ли затраченное время: соотношение выплат. Мы все видели ситуации, когда люди «просто если» останавливали немедленное кровотечение (и / или кричали), и, когда они складывались, вы получали запутанный код. Многие из нас также пережили чрезмерную абстракцию, которая остается загадкой, когда оригинальный кодер продолжает работать, что также приводит к запутанному коду.
Я бы посмотрел на вашу ситуацию и задал следующие вопросы:
Является ли этот код критически важным, и будет ли он значительно более стабильным, если я перекодирую? Говорят в хирургии, это рефакторинг спасения жизни, или это просто выбор и косметика?
Рассматриваю ли я рефакторинг кода, который мы собираемся заменить через 6 месяцев?
Готов ли я потратить столько времени на документирование дизайна и моих рассуждений для будущих разработчиков, сколько я потрачу на рефакторинг?
Что касается моего элегантного дизайна для добавления будущих функций, это код, который пользователи запрашивают изменения каждую неделю, или это первый раз, когда я коснулся этого в этом году?
Бывают времена, когда YAGNI и KISS выигрывают день, но бывают дни, когда фундаментальные перемены приведут вас из нисходящей спирали в дурь. Пока вы реалистичны в оценке не только того, что вы хотите, но и того, что другие должны будут делать для поддержания вашей работы, вы сможете лучше определить, какая ситуация и какая. О, и не забудьте записать, что вы сделали и почему. Это спасет и тех, кто следует за вами, и вас самих, когда вам придется вернуться позже.
источник
Во втором издании Страуструпса «Язык программирования C ++» (у меня нет доступной страницы), я прочитал
и я пошел хорошо, следуя совету. Конечно, есть компромиссы, и вы должны найти баланс, но короткие фрагменты лучше поддаются тестированию, чем большой беспорядок спагетти.
Я часто сталкивался с тем, что, различая один случай на два случая, если вы думаете о 2 как о n-случаях, вы открываете дверь для многих новых возможностей, о которых вы, возможно, даже не задумывались.
Но тогда вы должны задать вопрос ЯГНИ: оно того стоит? Будет ли это действительно полезно? Быть опытным означает, что вы редко будете ошибаться, а новичок чаще ошибаетесь.
Вы должны быть достаточно критичны, чтобы распознать шаблон и определить, трудно ли поддерживать ваш код из-за слишком большой абстракции или из-за того, что его сложно поддерживать, потому что все решается на месте.
Решение не в том или ином, а в том, чтобы думать об этом. :)
источник
«Хороший код, который может только A, хуже плохого кода, который может A, B, C и D.»
Это может иметь некоторый смысл в разработке продукта; Но большинство ИТ-специалистов работают в «проектах», а не в разработке продуктов.
В «ИТ-проектах», если вы запрограммируете хороший компонент, он будет функционировать бесперебойно в течение всего жизненного цикла проекта - который может закончиться не дольше, чем через 5 или 10 лет, тогда бизнес-сценарий может устареть и новый проект / ERP продукт мог бы заменить его. В течение этого 5/10 лет, если только в вашем коде нет дефектов, никто не заметит его существования, и достоинства ваших лучших мыслей останутся незамеченными! (если вы не умеете громко звучать на собственной трубе!)
Не многие получают возможность программировать «Windows Ctl + Alt + Del», и те немногие, кто получает этот шанс, могут не реализовать потенциал своего кода в будущем!
источник
Многие книги по бережливому и / или гибкому развитию помогут закрепить этот подход: делайте то, что необходимо прямо сейчас. Если вы знаете, что строите фреймворк, добавьте абстракции. В противном случае не добавляйте сложность, пока она вам не понадобится. Я рекомендую Lean Software Development , которая представит много других концепций, которые могут существенно повлиять на производительность.
источник
Забавно, как люди говорят о правильном / неправильном способе ведения дел. В то же время задача программирования все еще мучительно трудна и не дает хороших решений для написания больших сложных систем.
Возможно, когда-нибудь мы, программисты, наконец-то поймем, как писать сложные программы. До тех пор я советую всегда начинать с «глупой» реализации прототипа, а затем тратить достаточно времени на рефакторинг, чтобы ваши колледжи могли следовать вашему коду.
источник
Увидев такие преждевременно обобщенные конструкции, которые совсем не соответствовали фактическим требованиям, которые возникли позже, я разработал для меня правило:
Для гипотетических требований пишите только гипотетический код.
То есть: вам рекомендуется подумать об изменениях, которые могут произойти позже. Но используйте эти идеи только для выбора дизайна кода, который можно легко изменить и изменить, если эти требования действительно возникнут. Вы можете даже написать в своей голове некоторый код (гипотетический код), который вы хотели бы написать в этом случае, но не пишите никакого реального кода!
источник
Я думаю, что мышление, которое поможет вам, это всегда стремиться к конкретным решениям проблем кодирования вместо абстрактных решений. Абстракции следует добавлять только тогда, когда они действительно помогают упростить базу кода (например, когда они позволяют уменьшить базу кода).
Многие программисты обнаружили, что абстракции появляются почти самостоятельно, когда они сушат свой код. Шаблоны проектирования и лучшие практики помогут вам найти возможности для этого, но сами по себе не являются целями.
источник
Я думаю, что чрезмерное проектирование часто кажется небезопасным в написании кода. Все абстрактные принципы и шаблоны должны рассматриваться как инструменты, которые помогут вам. Часто случается так, что к ним относятся как к стандартам, которым нужно соответствовать.
Я считаю, что программист всегда в лучшем положении, чтобы решить, как абстрагироваться, чем аксиома.
Об остальном уже сказал KeithS
источник
Спросите себя, каковы преимущества хорошего дизайна:
Теперь спросите себя, действительно ли добавление всех этих слоев абстракций добавляет к любому из пунктов, упомянутых выше. Если это не так, вы делаете это НЕПРАВИЛЬНО .
Если вы можете добавить новые функции, добавив 3 строки кода, например:
Тогда, пожалуйста, пожалуйста, сделайте это. Это указывает на то, что ваш предыдущий дизайн был хорош и легко адаптировался. Только после того, как ваши классы начнут расти дальше определенного уровня, используйте рефакторинг для разделения функций и, возможно, для извлечения новых классов.
Мое эмпирическое правило заключается в том, что новые функции должны быть реализованы настолько минималистично, насколько это возможно, только тогда, когда что-то слишком велико, чтобы понять его заранее (скажем, на реализацию потребуется более 1 дня или полдня), вы можете добавить грубый глобальный дизайн. С этого момента добавляйте слои абстракции только тогда, когда размер кода увеличивается. Вы потом рефакторинг потом! Через некоторое время это может даже прийти к вам естественным, когда вам нужно немного придумать дизайн или пойти по быстрому пути. Еще один признак того, что некоторый код может использовать некоторую очистку, - это когда вы используете его повторно Каждый раз, когда вы добавляете новую функцию или вызываете старый код на новом месте, самое время взглянуть на старый код и посмотреть, сможете ли вы его немного улучшить, прежде чем добавлять снова. Таким образом, горячий код постепенно станет более чистым, в то время как неинтересные части медленно сгниют и не будут тратить ваше драгоценное время.
Если вы работаете так, вы никогда не будете переоценивать что-либо. Может потребоваться некоторая дисциплина, чтобы вернуться к старому коду, когда вы хотите добавить что-то новое, или оставить новый код немного более уродливым, чем вам нравится, но вы будете работать над чем-то продуктивным, а не чрезмерно спроектированным.
источник