Сколько шаблонов проектирования и уровней абстракции необходимо? [закрыто]


29

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

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

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

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

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

Пример, когда общий слой кэширования написан с использованием Memcache. Действительно ли нам нужно Memcache, MemcacheAdapter, MemcacheInterface, AbstractCache, CacheFactory, CacheConnector, ... или это легче поддерживать и по- прежнему хороший код при использовании только половина из этих классов?

Нашел это в твиттере:

введите описание изображения здесь

( https://twitter.com/rawkode/status/875318003306565633 )


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


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

8
Ни один ответ SE не может адекватно охватить эту тему. Это займет буквально годы опыта и наставничества, чтобы справиться. Это явно слишком широко.
jpmc26

5
Следуйте базовому принципу проектирования в авиационной промышленности: «Упростите и добавьте больше легкости». Когда вы обнаружите, что лучший способ исправить ошибку - это просто удалить код, содержащий ошибку, потому что она по-прежнему не делает ничего полезного, даже если она не содержит ошибок, вы начинаете понимать дизайн правильно!
Алефзеро

Ответы:


52

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

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

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

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


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

Количество абстракций даже не будет одинаковым для разных частей одного и того же проекта!
Т. Сар - Восстановить Монику

Подсказка: переход с Memcache на другой механизм кэширования (Redis?) - это изменение реализации .
Огр псалом33

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

Я бы сказал, что в случае двух интерфейсов и сотен классов, реализующих их, вы, вполне возможно , перегнули свои абстракции. Я, конечно, видел это в проектах, которые многократно используют очень расплывчатую абстракцию ( interface Doer {void prepare(); void doIt();}), и это становится болезненным для рефакторинга, когда эта абстракция больше не подходит. Ключевой частью ответа является тест, применяемый, когда абстракция должна измениться - если она никогда не меняется, она никогда не причиняет боль.
James_pic

24

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

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

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

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


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

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

Я хочу армию обученных белок для моего взлома ореха.
icc97

6

TL: DR;

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

Длинный ответ

Скорее всего, вы никогда не узнаете, на каком уровне абстракций вы строите.

Большинство уровней абстракции невидимы для нас, и мы принимаем их как должное.

Это рассуждение приводит меня к такому выводу:

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

Плохая абстракция придерживается колокола Гаусса в том факте, что, как только вы минуете сладкое пятно, начинает мешать. Хорошая абстракция, с другой стороны, невидима, и ее не может быть слишком много, потому что вы ее не замечаете. Подумайте о том, сколько уровней на уровнях API, сетевых протоколов, библиотек, библиотек ОС, файловых систем, уровней аппаратного обеспечения и т. Д., На которых построено ваше приложение и которое воспринимается как должное.

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


2
«Скорее всего, вы никогда не узнаете, на каком уровне абстракций вы строите». - На самом деле, весь смысл абстракции в том, что вы не знаете, как она реализована, поэтому вы не знаете (и не можете ) знать, сколько уровней абстракций она скрывает.
Йорг Миттаг

4

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

Абстракции как заборы. Они помогают разделить области вашей программы на тестируемые и взаимозаменяемые блоки (требования для создания нехрупкого нежесткого кода). И так же, как заборы:

  • Вы хотите абстракции в естественных точках интерфейса, чтобы минимизировать их размер.

  • Вы не хотите их менять.

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

  • Наличие одного в неправильном месте хуже, чем отсутствие его.

  • У них не должно быть больших утечек .


4

Рефакторинг

Я не видел слово «рефакторинг», упомянутое ни разу до сих пор. Итак, поехали:

Не стесняйтесь внедрять новую функцию как можно более прямо. Если у вас есть только один простой класс, вам, скорее всего, не нужен интерфейс, суперкласс, фабрика и т. Д.

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

Шаблоны - инструмент разума

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

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

Библиотеки

Библиотеки, вероятно, являются единственной областью, где это может быть для того, чтобы ошибиться в сторону слишком большого количества вариантов на основе шаблонов, а не слишком малого. Изменение чего-либо с «толстого» класса на что-то с более производным от шаблона (обычно это означает, что больше и меньше классов) радикально изменит интерфейс; и это единственное, что вы обычно не хотите менять в библиотеке, потому что это единственное, что представляет реальный интерес для пользователя вашей библиотеки. Им было бы наплевать на то, как вы справляетесь с вашей функциональностью внутри, но они очень заботятся, если им постоянно приходится менять свою программу, когда вы делаете новую версию с новым API.


2

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

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

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

После рассмотрения значения абстракций для программистов-потребителей мы можем обратиться к оценке и рассмотрению реализации, такой как реализация DRY-ness.

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


Пример, когда общий слой кэширования написан с использованием Memcache. Нужны ли нам Memcache, MemcacheAdapter, MemcacheInterface, AbstractCache, CacheFactory, CacheConnector, ... или это легче поддерживать и при этом сохранять хороший код при использовании только половины этих классов?

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


2

Абстракция предназначена для облегчения понимания кода. Если слой абстракции сделает вещи более запутанными - не делайте этого.

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

  • минимизировать время разработки
  • максимизировать поддержку кода

Аннотация только при необходимости

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

Не абстрагируйся когда

  1. Это не даст преимущества в повторном использовании кода или ясности
  2. Это сделает код значительно длиннее / сложнее без какой-либо выгоды.

Некоторые примеры

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

2

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

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

У вас есть этот конкретный сценарий. Организуйте свой код таким образом. Вот умный, но несколько надуманный пример.

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

В дополнение к этому, я хотел бы процитировать Андрея Александреску, говоря о разработке программного обеспечения, который заявляет:

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

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

В такие времена в моей голове кричит пророческий голос Майка Актона, ведущего игрового движка в Insomniac:

ЗНАЙТЕ СВОИ ДАННЫЕ

Он говорит о входах в вашу программу и желаемых результатах. И еще есть этот драгоценный камень Фреда Брукса из Мистического Месяца Человека:

Покажи мне свои блок-схемы и спрячь свои таблицы, и я буду продолжать мистифицироваться. Покажите мне свои таблицы, и мне обычно не нужны ваши блок-схемы; они будут очевидны.

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

  • Являются ли выходные данные из моей программы правильными?
  • Производится ли оно эффективно / быстро для моего наиболее распространенного случая ввода?
  • Достаточно ли легок мой код для локального анализа, как для меня, так и для моих товарищей по команде? Если нет, то могу ли я сделать это проще?

Когда вы это сделаете, вопрос о том, «сколько слоев абстракции или шаблонов проектирования необходимо» становится намного проще. Сколько слоев абстракции вам нужно? Столько, сколько необходимо для достижения этих целей, и не более. «А как насчет шаблонов дизайна? Я не использовал ни одного!» Что ж, если вышеуказанные цели были достигнуты без непосредственного применения шаблона, то это нормально. Заставьте это работать, и переходите к следующей проблеме. Начните с ваших данных, а не с кода.


2

Архитектура программного обеспечения изобретает языки

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

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

читабельность

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

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

Представленные вами концепции не должны содержать неожиданностей. Это в основном соглашения об именах, такие как методы get, set и т. Д. И шаблоны проектирования могут помочь, поскольку они предоставляют стандартный шаблон решения, а читатель видит «ОК, я получаю объекты с фабрики» и знает, что это значит. Но если простая инстанцияция конкретного класса делает свою работу, я бы предпочел это.

Удобство использования

Язык должен быть простым в использовании (что позволяет легко формулировать «правильные предложения»).

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

Предоставление только необходимых классов / методов облегчает пользователю поиск того, что ему нужно (см. Цитату Антуана де Сент-Экзюпери DocBrowns). Предоставление интерфейса вместо реализующего класса может сделать это проще.

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

Резюме

Ввести абстракции, если это облегчает использование (и стоит затрат на поддержание как абстракции, так и реализации).

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


1

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

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


-1

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

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


2
Давайте воздержимся от отклонения своего опыта как «фальшивого». Слишком много абстракций - настоящая проблема. К сожалению, люди добавляют абстракцию заранее, потому что это «лучшая практика», а не решение реальной проблемы. Кроме того, никто не может решать "об этих вещах" ... люди уходят из компаний, люди присоединяются, никто не становится владельцем их грязи.
Rawkode
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.