ТВЕРДЫЙ против избежания преждевременной абстракции

27

Я понимаю, что SOLID должен выполнять, и регулярно использую его в ситуациях, когда модульность важна и ее цели явно полезны. Однако две вещи мешают мне применять его последовательно в моей кодовой базе:

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

  • Мне трудно оправдать увеличение модульности ради самого себя, если это делает мой код более многословным, трудным для понимания и т. Д. И не устраняет никакого дублирования. Я считаю, что простой, тесно связанный процедурный или объектный код Бога иногда легче понять, чем очень хорошо продуманный код равиоли, потому что поток простой и линейный. Это также намного легче написать.

С другой стороны, это мышление часто приводит к объектам Бога. Я обычно достаточно осторожно рефакторинг, добавляя четкие линии абстракции только тогда, когда вижу четкие шаблоны. Что, если вообще что-то не так с объектами God и тесно связанным кодом, если вам явно не нужно больше модульности, нет значительного дублирования и код читабелен?

РЕДАКТИРОВАТЬ: Что касается отдельных принципов SOLID, я хотел подчеркнуть, что подстановка Лискова ИМХО является формализацией здравого смысла и должна применяться везде, поскольку абстракции не имеют смысла, если это не так. Кроме того, каждый класс должен нести одну ответственность на некотором уровне абстракции, хотя это может быть очень высокий уровень с деталями реализации, объединенными в один огромный класс из 2000 строк. По сути, ваши абстракции должны иметь смысл там, где вы решите абстрагироваться. Принципы, которые я подвергаю сомнению в случаях, когда модульность явно не полезна, являются открытыми-закрытыми, сегрегацией интерфейса и особенно инверсией зависимостей, поскольку они касаются модульности, а не просто наличия абстракций.

dsimcha
источник
10
@ S.Lott: ключевое слово есть «больше». Это именно то, для чего была изобретена ЯГНИ. Увеличение модульности или уменьшение сцепления просто ради самого себя - это просто культовое программирование.
Мейсон Уилер
3
@ С.Лотт: Это должно быть очевидно из контекста. Насколько я понимаю, по крайней мере, «больше, чем в настоящее время существует в рассматриваемом коде».
Мейсон Уилер
3
@ S.Lott: Если вы настаиваете на педантичности, «больше» означает больше, чем существует в настоящее время. Подразумевается, что что-то, будь то код или грубый дизайн, уже существует, и вы думаете о том, как его реорганизовать.
dsimcha
5
@ back2dos: Точно, но я скептически отношусь к разработке чего-то для расширяемости без четкого представления о том, как это может быть расширено. Без этой информации ваши линии абстракции, вероятно, окажутся во всех неправильных местах. Если вы не знаете, где строки абстракции могут быть полезны, я предлагаю вам просто написать наиболее лаконичный код, который работает и доступен для чтения, даже если у вас есть несколько объектов God.
dsimcha
2
@dsimcha: Вам повезло, если требования изменились после того, как вы написали кусок кода, потому что обычно они меняются, когда вы это делаете. Вот почему реализации моментальных снимков не подходят для того, чтобы пережить реальный жизненный цикл программного обеспечения. Кроме того, SOLID не требует, чтобы вы просто абстрагировались вслепую. Контекст для абстракции определяется через инверсию зависимостей. Вы уменьшаете зависимость каждого модуля до самой простой и понятной абстракции других сервисов, на которые он опирается. Это не произвольно, искусственно или сложно, но четко определено, правдоподобно и просто.
back2dos

Ответы:

8

Ниже приведены простые принципы, которые вы можете применить, чтобы помочь вам понять, как сбалансировать дизайн вашей системы:

  • Принцип единой ответственности: Sв SOLID. Вы можете иметь очень большой объект с точки зрения количества методов или объема данных и при этом соблюдать этот принцип. Возьмите, например, объект Ruby String. Этот объект имеет больше методов, чем вы можете потрясти палкой, но у него все еще есть только одна обязанность: держать текстовую строку для приложения. Как только ваш объект начинает брать на себя новую ответственность, задумайтесь об этом. Вопрос обслуживания спрашивает себя: «Где бы я мог найти этот код, если бы у меня были проблемы с ним позже?»
  • Простота важна: Альберт Эйнштейн сказал: «Сделай все как можно проще, но не проще». Вам действительно нужен этот новый метод? Можете ли вы выполнить то, что вам нужно, с существующей функциональностью? Если вы действительно думаете, что должен быть новый метод, можете ли вы изменить существующий метод, чтобы удовлетворить все то, что вам нужно? По сути рефакторинг, как вы строите новые вещи.

По сути, вы пытаетесь сделать все возможное, чтобы не застрелиться в ногу, когда приходит время поддерживать программное обеспечение. Если ваши большие объекты являются разумными абстракциями, то нет причин для их разделения просто потому, что кто-то придумал метрику, которая говорит, что класс должен быть не более чем X строк / методов / свойств / и т.д. Во всяком случае, это руководящие принципы, а не жесткие и быстрые правила.

Берин Лорич
источник
3
Хороший комментарий Как обсуждалось в других ответах, одна проблема с «единственной ответственностью» заключается в том, что ответственность класса может быть описана на различных уровнях абстракции.
dsimcha
5

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

Как и во всех других творческих дисциплинах, здесь нет абсолютов - только компромиссы. Чем больше вы делаете это, тем «легче» становится решать, когда этого достаточно для вашей текущей проблемной области или требований.

Сказав это - абстракция - это сердце разработки программного обеспечения - поэтому, если нет веской причины не делать этого, то практика сделает вас лучше в этом, и вы почувствуете внутреннее понимание компромиссов. Так одобрите это, если это не становится вредным.

Стивен Бейли
источник
5
I want to avoid premature abstraction.In my experience drawing abstraction lines without concrete use cases... 

Это частично правильно и частично неправильно.

Неверно
Это не является причиной, препятствующей применению принципов OO / SOLID. Это только мешает применять их слишком рано.

Который...

Право
Не рефакторинг кода, пока он не рефакторинг; когда это требования выполнены. Или когда «варианты использования» все там, как вы говорите.

I find simple, tightly coupled procedural or God object code is sometimes easier to understand than very well-factored...

При работе в одиночку или в бункере
. Проблема с отсутствием ОО не сразу очевидна. После того, как вы напишите первую версию программы:

Code is fresh in your head
Code is familiar
Code is easy to mentally compartmentalize
You wrote it

3/4 из этих хороших вещей быстро умирают за короткое время.

Наконец, вы узнаете, что у вас есть объекты Бога (объекты со многими функциями), поэтому вы распознаете отдельные функции. Инкапсуляция. Поместите их в папки. Используйте интерфейсы время от времени. Сделайте себе одолжение для долгосрочного обслуживания. Тем более что нерефакторированные методы имеют тенденцию бесконечно раздуты и превращаться в методы Бога.

В команде
Проблемы с отсутствием ОО сразу заметны. Хороший ОО-код как минимум самодокументирован и читабелен. Особенно в сочетании с хорошим зеленым кодом. Кроме того, отсутствие структуры затрудняет разделение задач и интеграцию намного сложнее.

P.Brian.Mackey
источник
Правильно. Я смутно осознаю, что мои объекты делают больше, чем одно, но я не понимаю более конкретно, как полезно разбивать их на части, чтобы линии абстракции находились в нужных местах, когда необходимо выполнить обслуживание. Рефакторинг кажется пустой тратой времени, если программисту обслуживания, вероятно, придется делать гораздо больше рефакторинга, чтобы в любом случае получить строки абстракции в нужном месте.
dsimcha
Инкапсуляция и абстракция - это две разные концепции. Инкапсуляция - это базовая концепция ОО, которая в основном означает, что у вас есть классы. Классы обеспечивают модульность и удобочитаемость сами по себе. Это полезно
P.Brian.Mackey
Абстракция может уменьшить обслуживание при правильном применении. При неправильном применении это может увеличить техническое обслуживание. Знание того, когда и где проводить черты, требует глубокого понимания бизнеса, структуры, клиента в дополнение к основным техническим аспектам того, как «применять фабричный шаблон» как таковой.
P.Brian.Mackey
3

Нет ничего плохого в больших объектах и ​​тесно связанном коде, когда они уместны и когда не требуется ничего более совершенного . Это просто еще одно проявление эмпирического правила, превращающегося в догму.

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

Мейсон Уилер
источник
1
+1. Еще нужно хорошее определение того, что означает «соответствующий».
jprete
2
@jprete: почему? Попытка применить абсолютные определения является частью причины концептуальных беспорядков, подобных этой. В создании хорошего программного обеспечения много мастерства. Что действительно нужно IMO, так это опыт и здравый смысл.
Мейсон Уилер
4
Ну, да, но широкое определение какого - то еще пригодится.
jprete
1
Было бы полезно иметь соответствующее определение, но это скорее главное. Трудно придать точный характер, потому что это применимо только в каждом конкретном случае. Сделаете ли вы правильный выбор в каждом конкретном случае, во многом зависит от опыта. Если вы не можете четко сформулировать, почему вы обладаете высокой когезией, монолитное решение лучше, чем низкоцепное высокомодульное решение, вам «вероятно» будет лучше, если у вас низкая когезия и модульность. Рефакторинг всегда проще.
Ян
1
Я предпочитаю, чтобы кто-то слепо писал разъединенный код, а не слепо писал код для спагетти :)
Борис Янков
2

Я имею тенденцию подходить к этому больше с точки зрения «Ты не будешь нуждаться в этом». Этот пост будет посвящен, в частности, одному вопросу: наследованию.

В моем первоначальном проекте я создаю иерархию вещей, которые, как я знаю, будут двух или более. Скорее всего, им понадобится большая часть одного и того же кода, поэтому стоит разработать его с самого начала. После того, как начальный код установлен , и мне нужно добавить больше функций / возможностей, я смотрю на то, что у меня есть, и думаю: «Имеет ли что-то, что я уже реализовал, ту же или подобную функцию? Если это так, то, скорее всего, это новая абстракция, требующая выпуска.

Хорошим примером этого является инфраструктура MVC. Начиная с нуля, вы создаете одну большую страницу с кодом позади. Но тогда вы хотите добавить еще одну страницу. Однако один код позади уже реализует много логики, необходимой новой странице. Итак, вы абстрагируете Controllerкласс / интерфейс, который будет реализовывать логику, специфичную для этой страницы, оставляя общие вещи в исходном «богом» коде.

Майкл К
источник
1

Если вы знаете, как разработчик, когда НЕ следует применять принципы из-за будущего кода, тогда это хорошо для вас.

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

Что еще более важно, учтите, что большинство разработчиков делают ежедневную работу и не заботятся так же, как вы. Если вы изначально установили долго работающие методы, что другие разработчики, которые придут к коду, подумают, когда они будут поддерживать или расширять его? ... Да, как вы уже догадались, BIGGER-функции, LONGER-запущенные классы и MORE объекты бога, и у них нет принципов, чтобы можно было уловить растущий код и правильно его инкапсулировать. Ваш «читаемый» код теперь превращается в гниющий беспорядок.

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

Я не особенно возражаю против глобальной «Карты реестра» или «Объекта Бога», если они просты. Это действительно зависит от конструкции системы, иногда вы можете сойти с рук и оставаться простым, а иногда нет.

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

Мартин Блор
источник
1
В основном хороший ответ. Единственное, что меня беспокоит, так это то, что если какой-то разработчик технического обслуживания приходит и делает беспорядок, это его / ее вина в том, что он не рефакторинг, если такие изменения не казались достаточно вероятными в предвидении, чтобы оправдать планирование для них, или код был настолько плохо документирован / протестирован / и т.д. , этот рефакторинг был бы формой безумия.
dsimcha
Это правда дсимча. Просто я подумал, что в системе, где разработчик создает папку «RequestHandlers», и каждый класс в этой папке является обработчиком запросов, затем следующий разработчик, который приходит и думает: «Мне нужно добавить новый обработчик запросов», существующая инфраструктура почти заставляет его следовать по маршруту. Я думаю, что это то, что я пытался сказать. Установление очевидного соглашения с самого начала может помочь даже самым неопытным разработчикам продолжить работу.
Мартин Блор,
1

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

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

jprete
источник
«Делать больше, чем одно» может быть сложно определить, в зависимости от того, на каком уровне абстракции вы работаете. Можно утверждать, что любой метод с двумя или более строками кода делает больше, чем одну вещь. На противоположном конце спектра для чего-то сложного, такого как механизм сценариев, потребуется множество методов, которые все делают отдельные «вещи», которые не связаны напрямую друг с другом, но каждый из них составляет важную часть «мета- вещь ", которая заставляет ваши скрипты работать должным образом, и есть только так много разрыва, которые можно сделать, не нарушая механизм скриптов.
Мейсон Уилер
Определенно существует спектр концептуальных уровней, но мы можем указать на это: размер «класса, который делает одно» должен быть таким, чтобы вы могли почти сразу понять реализацию. Т.е. детали реализации вписываются в вашу голову. Если он станет больше, вы не сможете понять весь класс сразу. Если он меньше, значит, вы слишком далеко абстрагируетесь. Но, как вы сказали в другом комментарии, здесь много суждений и опыта.
jprete
ИМХО, лучший уровень абстракции для размышления - это уровень, который вы ожидаете от потребителей объекта. Допустим, у вас есть объект с именем, Queryerкоторый оптимизирует запросы к базе данных, запрашивает СУБД и анализирует результаты в возвращаемых объектах. Если есть только один способ сделать это в вашем приложении, и Queryerон герметически запечатан и инкапсулирует это одним способом, то это делает только одно. Если есть несколько способов сделать это, и кто-то может позаботиться о деталях одной его части, тогда он делает несколько вещей, и вы можете захотеть разделить его.
dsimcha
1

Я использую более простое руководство: если вы можете написать для него модульные тесты и не иметь дублирования кода, это достаточно абстрактно. Кроме того, вы находитесь в хорошем положении для последующего рефакторинга.

Кроме того, вы должны помнить о SOLID, но скорее как руководство, а не как правило.

Джеффри Фауст
источник
0

Что, если вообще что-то не так с объектами God и тесно связанным кодом, если вам явно не нужно больше модульности, нет значительного дублирования и код читабелен?

Если приложение достаточно маленькое, все, что можно отремонтировать. В больших приложениях объекты Бога быстро становятся проблемой. В конечном итоге реализация новой функции требует модификации 17 объектов Бога. Люди не очень хорошо следуют 17-шаговым процедурам. Объекты Бога постоянно модифицируются несколькими разработчиками, и эти изменения необходимо объединять неоднократно. Вы не хотите идти туда.

Кевин Клайн
источник
0

Я разделяю ваши опасения по поводу чрезмерной и неуместной абстракции, но я не обязательно так обеспокоен преждевременной абстракцией.

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

Это подразумевает некоторую степень прототипирования, экспериментов и возврата в код.

Тем не менее, не существует простого детерминистического правила, которое могло бы решить все проблемы. Опыт имеет большое значение, но вы должны получить этот опыт, делая ошибки. И всегда есть больше ошибок, чтобы сделать, что предыдущие ошибки не подготовили вас к.

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

Хорошая работа - если бы кто-то мог предоставить четкий, детерминистический алгоритм для разработки и кодирования программ, для человека была бы только одна последняя программа - программа, которая автоматически напишет все будущие программы без вмешательства человека.

Steve314
источник
0

Рисование соответствующих линий абстракции - это то, что человек извлекает из опыта. Вы будете делать ошибки при этом, что неоспоримо.

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

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

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

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

Newtopian
источник
0

Важным моментом, который, кажется, упустили из виду, является то, что SOLID практикуется вместе с TDD. TDD имеет тенденцию выдвигать на первый план то, что "уместно" в вашем конкретном контексте, и помогает облегчить большую часть двусмысленности, которая, кажется, содержится в совете

delitescere
источник
1
Пожалуйста, рассмотрите вопрос о вашем ответе. В настоящее время вы объединяете две ортогональные концепции, и неясно, как результат отвечает интересам ОП.