Абстракт: война между решением проблемы и общим решением [закрыто]

33

Как программист, я попадаю в дилемму, где я хочу сделать свою программу максимально абстрактной и максимально общей.

Обычно это позволяет мне повторно использовать мой код и найти более общее решение для проблемы, которая может (или не может) появиться снова.

Тогда этот голос в моей голове говорит, просто решите проблему, пустышка, это так просто! Зачем тратить больше времени, чем нужно?

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

Что слушать и как часто? Какова ваша стратегия для этого? Стоит ли абстрагироваться от всего?

Брайан Харрингтон
источник
1
«Как абстрактный и общий» обычно называют гордостью программиста :) Однако из-за закона Мерфи одна степень адаптации, которая понадобится вам при работе над следующей задачей, будет той, которую вы не предоставили.
Матье М.
1
Один из лучших вопросов!
исследовать
1
Ты всегда догадываешься неправильно, так что просмотри это просто. Когда вы расширили простые случаи «достаточное количество раз», вы начинаете видеть шаблон. До тех пор, не надо.

Ответы:

27

Что слушать и как часто?

Никогда не абстрагируйтесь, пока не должны.

Например, в Java вы должны использовать интерфейсы. Они абстракция.

В Python у вас нет интерфейсов, у вас есть Duck Typing, и вам не нужны высокие уровни абстракции. Так что вы просто не делаете.

Какова ваша стратегия для этого?

Не абстрагируйтесь, пока не напишите три раза.

Один раз - хорошо - один раз. Просто решите это и двигайтесь дальше.

Дважды указывает на то, что здесь может быть образец. Или не может. Это может быть просто совпадение.

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

Стоит ли абстрагироваться от всего?

Нет.

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

Обычно это позволяет мне повторно использовать мой код

Это предположение, которое часто ложно. Абстракция не может помочь вообще. Это может быть сделано плохо. Поэтому не делайте этого, пока не должны.

С. Лотт
источник
1
«Никогда не абстрагируйся, пока не должен». - Я так понимаю, вы избегаете использования классов, методов, циклов или операторов if? Эти вещи все абстракции. Если вы думаете об этом, код, написанный на языках высокого уровня, является очень абстрактным, нравится вам это или нет. Вопрос не в том, стоит ли абстрагироваться. Это какие абстракции использовать.
Джейсон Бейкер
9
@ Джейсон Бейкер: «Я так понимаю, вы избегаете использования классов, методов, циклов или операторов if». Похоже, это не то, о чем был вопрос. Зачем делать абсурдное утверждение, что все программирование невозможно, если мы избегаем чрезмерного абстрагирования наших проектов?
S.Lott
1
Мне вспоминается старая шутка, где мужчина спрашивает женщину, не спит ли она с ним за миллион долларов. Когда она говорит «да», он спрашивает, будет ли она спать с ним за пять долларов. Когда она говорит: «За какую женщину ты меня принимаешь?» ответ: «Ну, мы уже установили, что вы будете спать с кем-то для секса. Теперь мы просто торгуемся по поводу цены». Моя точка зрения заключается в том, что это никогда не решение абстрагироваться или нет. Речь идет о том, сколько абстрагировать и какие абстракции выбрать. Вы не можете удержать абстракцию, если не пишете чистую сборку.
Джейсон Бейкер
9
@ Джейсон Бейкер: «Вы не можете отложить абстракцию, если не пишете чистую сборку». Это тривиально. Сборка - это абстракция над машинным языком. Который является абстракцией над аппаратной схемой. Пожалуйста, прекратите читать слишком много в ответе, который не был поставлен под вопрос. Вопрос был не об «абстракции» как о концепции или интеллектуальном инструменте. Речь шла об абстракции как ооо дизайн-методике - неправильно используемой - слишком часто. Мой ответ был не в том, что абстракция невозможна. Но это "чрезмерная абстракция" плохо.
S.Lott
1
@JasonBaker, это не неслыханная причина спать с кем-то. Возможно, вы думали о «деньгах»?
19

Ах, ЯГНИ. Наиболее злоупотребленная концепция программирования.

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

Как говорится "Делай самое простое, что могло бы сработать". Дело в том, что люди всегда путают простое с легким . Простота требует работы. Но это стоит усилий.

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

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

Джейсон Бейкер
источник
Хорошее различие между simpleи easy!
Матье М.
«Но мой опыт показывает, что мало компаний», что интересно, обратное может быть справедливо в научных кругах.
Стив Беннетт
12

Силлогизм:

  1. Общность дорогая.

  2. Вы тратите деньги других людей.

  3. Поэтому расходы на общность должны быть обоснованы для заинтересованных сторон.

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

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

И, наконец, не существует такого понятия, как «как можно более общее». Предположим, вы пишете программное обеспечение на C # только для аргументации. Собираетесь ли вы заставить каждый класс реализовывать интерфейс? Каждый класс абстрактный базовый класс с каждым методом абстрактный метод? Это довольно общее, но это далеко не «как можно более общее». Это позволяет людям изменять реализацию любого метода с помощью подклассов. Что если они захотят изменить реализацию метода без создания подклассов? Вы можете сделать каждый метод на самом деле свойством типа делегата с установщиком, чтобы люди могли изменить каждый метод на что-то другое. Но что, если кто-то захочет добавить больше методов? Теперь каждый объект должен быть расширяемым.

На данный момент вы должны отказаться от C # и принять JavaScript. Но вы все еще не получили достаточно общего; Что делать, если кто-то хочет изменить, как поиск членов работает для этого конкретного объекта? Может быть, вы должны написать все на Python вместо этого.

Повышение универсальности часто означает отказ от предсказуемости и массовое увеличение затрат на тестирование, чтобы гарантировать, что реализованная универсальность действительно успешно удовлетворяет реальные потребности пользователей . Оправданы ли эти затраты их преимуществами для заинтересованных сторон, которые за них платят? Возможно, они есть; Это зависит от вас, чтобы обсудить с заинтересованными сторонами. Мои заинтересованные стороны совсем не хотят, чтобы я отказался от статической типизации в поисках совершенно ненужного и чрезвычайно дорогого уровня общности, но, возможно, ваш.

Эрик Липперт
источник
2
+1 за полезные принципы, -1 за то, что все дело в деньгах.
Мейсон Уилер
4
@ Мейсон: Дело не в деньгах , а в усилиях . Деньги - это практическая мера усилий . Эффективность - это выгоды, получаемые за каждое произведенное усилие; опять же, прибыль в деньгах - это практическая мера начисленных выгод . Просто проще обсудить абстракцию денег, чем придумать способ измерить усилия, затраты и выгоду. Вы бы предпочли измерить усилие, стоимость и выгоду другим способом? Не стесняйтесь сделать предложение лучшего способа.
Эрик Липперт
2
Я согласен с мнением @Mason Wheeler. Я считаю, что решение здесь состоит в том, чтобы не привлекать заинтересованные стороны к этому уровню проекта. Очевидно, это сильно зависит от отрасли, но клиенты обычно не видят код. Кроме того, все эти ответы, кажется, против усилий, кажется ленивым.
Orbling
2
@ Orbling: я категорически против ненужных, дорогих, расточительных усилий, которые отнимают ресурсы у достойных и важных проектов. Если вы предполагаете, что это делает меня «ленивым», то я утверждаю, что вы используете слово «ленивый» необычным образом.
Эрик Липперт
1
По моему опыту, другие проекты не имеют тенденцию уходить, поэтому обычно стоит потратить столько времени, сколько необходимо в текущем проекте, прежде чем двигаться дальше.
Orbling
5

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

Например, рассмотрим функцию, которая копирует память.

Функция является абстракцией, которая скрывает, как копируются 4 байта.

int copy4Bytes (char * pSrc, char * pDest)

Обобщением было бы сделать эту функцию копировать любое количество байтов.

int copyBytes (char * pSrc, char * pDest, int numBytesToCopy)

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

Более конкретно, связанный с вашим вопросом, абстракция полезна не только с точки зрения повторного использования кода. Часто, если все сделано правильно, это сделает ваш код более читабельным и понятным. Используя приведенный выше пример, что легче читать и понимать, если вы просматриваете код copyBytes () или цикл for, перебирающий массив, перемещающий данные по одному индексу за раз? Абстракция может обеспечить своего рода документацию, которая, по моему мнению, облегчает работу с кодом.

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

Pemdas
источник
+1 за разницу между абстракцией и обобщением.
Йорис Мейс
4

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

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

Ларри Коулман
источник
1
Это очень похоже на мою версию правила рефакторинга: ударить два! рефакторинг!
Фрэнк Шиарар
@Frank: Это, вероятно, ваше правило рефакторинга, но со вторым абзацем, добавленным из личного опыта.
Ларри Коулман
+1: я полагаю, что это также отмечено довольно рано в Прагматическом Программисте почти дословно IIRC.
Стивен Эверс
о, конечно Нет ничего, чтобы абстрагироваться только одним примером: как только у вас появится второй пример, вы сможете увидеть, что является общим (общим), а что нет, и вы можете абстрагироваться!
Фрэнк Шиарар
Выборка из двух, на мой взгляд, слишком мала, чтобы обобщать на бесконечность. Другими словами, слишком рано.
2

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

Первое: преждевременная общность стоит дорого.

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

Третье: держите ваши политики подальше от ваших механизмов.

Конрад Фрикс
источник
1

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

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

Роб Вейр
источник
0

Я большой поклонник принципа KISS.

Я сосредоточен на предоставлении того, что меня просят сделать, а не на том, что является лучшим решением. Я должен был отпустить перфекционизм (и ОКР), потому что он сделал меня несчастным.

Pablo
источник
Почему нелюбовь к перфекционизму? (Кстати, я вас не голосовал)
Orbling
Не стремление к совершенству работает для меня. Зачем? Потому что я знаю, что мои программы никогда не будут идеальными. Стандартного определения совершенства не существует, поэтому я просто хочу предложить то, что хорошо работает.
Пабло
-1

где абстракция находится на вашем правом плече, а Solve-it-глупый - на левом.

Я не согласен с «решить это глупо» , я думаю, что это может быть более «решить это умно» .

Что умнее:

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

Выбор по умолчанию должен быть вторым. Если вы не можете продемонстрировать потребность в поколениях.

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

Обычно я считаю, что обобщенные решения лучше всего использовать в качестве «основного кода библиотеки»

Темная ночь
источник
Если бы вы могли сделать все предположения здесь, у вас не было бы проблемы. Короткие и простые в обслуживании являются взаимоисключающими. Если вы знаете, что что-то будет использовано повторно, общее решение будет направлено на это.
JeffO
@ Джефф Я не совсем уверен, что понимаю ваш комментарий ..
Darknight
Вы вынули «Решить это глупо» из контекста.
Брайан Харрингтон