2010-07-14 2 views
12

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

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

Одним из таких консервативное отношение было выражено мне недавно:

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

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

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

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

Итак, что вы думаете, ТАК? Что вы считаете чрезмерно обобщающим? Имеет ли это правило различную применимость в зависимости от контекста? Вы даже согласны, что это правило?

+2

Вы код для жизни? Как работает этот веб-сервер на базе Haskell? Помните: «умный И ПОЛУЧИТЕ ВЕЩИ». http://www.joelonsoftware.com/items/2007/06/05.html –

+0

@ Джеймс Макнеллис: спасибо. Я не хотел указывать пальцами или казаться, будто я просто пытался выкопать старые аргументы. Мне действительно интересно. – Cogwheel

+1

Вот сделка - вы больше не студент колледжа. Я надеюсь, что вы всегда можете превратить определенную функцию в общую, если возникнет настоящая необходимость. Теперь вы занятый взрослый с работой, которая платит вам 50 долларов за час. Разве не имеет смысла обобщать только тогда, когда вам нужно? Время - деньги. –

ответ

3

Что-то слишком обобщенно, когда вы теряете время, обобщая его. Если вы собираетесь использовать общие функции в будущем, вы, вероятно, не будете тратить время. Это действительно так просто [на мой взгляд].

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

12

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

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

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

+0

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

10

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

Существует принцип, согласно которому люди XP следуют под названием YAGNI - вам это не понадобится.

wiki это сказать:

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

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

+1

Если бы это было не субъективно, я бы принял это как ответ. Первое предложение касается ногтей прямо на моей голове. – Cogwheel

+0

# @ &%. Я просто провел неделю, обобщая то, чего я действительно не должен был. Неудивительно, что я никогда ничего не делал. – mpen

0

есть 2 примеры из Microsoft в более-обобщения:
1.) CObject (MFC)
2.) Объект (.Net)

оба из них используются для "понимают" generics в C++, которые большинство людей не использует. На самом деле, все делали проверку по параметру, заданному с использованием этих (CObject/Object).

2

Я думаю, вы должны рассмотреть два основных принципа программирования: KISS (держите его простым и понятным) и DRY (не повторяйте себя) , Большую часть времени я начинаю с первого: реализовать необходимую функциональность наиболее простым и простым способом. Довольно часто этого достаточно, потому что он уже может удовлетворить мои требования. В этом случае он остается простым (и не общим).

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

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

Однако могут быть некоторые исключительные случаи.
a) Когда общее решение почти точно такое же усилие и сложность. Пример: запись реализации очереди с использованием дженериков не намного сложнее, чем для строк.
b) Если проще решить проблему в общем виде, а также легче понять решение. Это происходит не слишком часто, я не могу придумать простой пример реальной жизни на данный момент :-(Но даже в этом случае использование/анализ конкретных примеров ранее было обязательным IMO, поскольку только это может подтвердить что вы на правильном пути.

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

Если у вас есть время, вы можете взглянуть на Structure and Interpretation of Computer Programs. В нем много интересного о том, как найти правильный баланс между общей и сложной сложностью и как сохранить сложность как минимум, которая действительно требуется вашим проблема.

И, конечно же, различные гибкие процессы также рекомендуют нечто подобное: начните с простого, рефакторинга, когда это необходимо.

6

Слишком общее? Я должен признать, что я поклонник Generic Programming (как принцип), и мне очень нравится идея, что Haskell и Go используют там.

При программировании на C++, однако, вам предлагают два пути для достижения аналогичных целей:

  • Обобщенное программирование: по пути шаблонов, даже если есть проблемы со временем компиляции, зависимость от реализации, и т.д. ..
  • объектно-ориентированное программирование: его предок таким образом, что ставит вопрос о самом (класс/структура) объекта, а не на функцию ...

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

Исходя из опыта, я бы сказал, что чем меньше функция/класс, тем более основной целью является ее обобщение. В качестве примера я провожу набор инструментов в большинстве моих домашних проектов и на работе. Большинство функций/классов есть общий ... немного как Boost, таким образом,)

// No container implements this, it's easy... but better write it only once! 
template <class Container, class Pred> 
void erase_if(Container& c, Pred p) 
{ 
    c.erase(std::remove_if(c.begin(), c.end(), p), c.end()); 
} 

// Same as STL algo, but with precondition validation in debug mode 
template <class Container, class Iterator = typename Container::iterator> 
Iterator lower_bound(Container& c, typename Container::value_type const& v) 
{ 
    ASSERT(is_sorted(c)); 
    return std::lower_bound(c.begin(), c.end(), v); 
} 

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

Вот почему я сам ценю принцип наименьшего усилия. Когда я думаю о классе или методе, я сначала делаю шаг назад и думаю немного:

  • Будет ли иметь смысл, чтобы оно было более общим?
  • Какая бы цена?

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

Пример:

void Foo::print() { std::cout << /* some stuff */ << '\n'; } 

// VS 

std::ostream& operator<<(std::ostream& out, Foo const& foo) 
{ 
    return out << /* some stuff */ << '\n'; 
} 

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

+1

_Всегда это не намного больше, чем ощущение кишки: _ Согласен. –

2

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

Object saveOrUpdate(Object object) 

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

Смежные вопросы