Как избежать бесконечно повторяющихся одинаково неоптимальных проектов?

10

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


Пример:

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

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

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

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


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

Изменить: Кажется, есть несколько толкований вопроса, которые я хочу прояснить:

  • Я полностью исключил ООП, поскольку выясняется, что он на самом деле не является специфическим для ООП, плюс слишком легко неверно истолковать некоторые из комментариев, которые я сделал при прохождении по поводу ООП.
  • Некоторые утверждают, что я должен использовать итеративный подход и пробовать разные шаблоны, или что мне следует отказаться от шаблона, когда он перестает работать. Это процесс, на который я собирался ссылаться в первую очередь. Я думал, что это было ясно из примера, но я мог бы сделать это более ясным, поэтому я отредактировал вопрос, чтобы сделать это.
Джонатан
источник
3
Не «подписывайтесь на философию ОО». Это инструмент, а не главное жизненное решение. Используйте это, когда это помогает, и не используйте это, когда это не делает.
user253751
Если вы застряли, думая об определенных шаблонах, возможно, попробуйте написать какой-нибудь умеренно сложный проект на C, будет интересно узнать, как много вы можете сделать без этих шаблонов.
user253751
2
У С есть шаблоны. Это просто очень разные модели. :)
candied_orange
Я исправил большинство из этих неправильных толкований в редактировании
Джонатан
Иметь эти проблемы время от времени нормально. Часто они не должны иметь место. Скорее всего, вы используете неправильную парадигму программирования для проблемы, или ваши проекты представляют собой смесь парадигм в неправильных местах.
Данк

Ответы:

8

Когда я принимаю такое жесткое решение, я обычно задаю себе три вопроса:

  1. Каковы плюсы и минусы всех доступных решений?

  2. Есть ли решение, которое я еще не рассматривал?

  3. Самое главное:
    каковы мои требования? Не требования на бумаге, настоящие фундаментальные?
    Могу ли я как-то переформулировать проблему / настроить ее требования, чтобы она позволила получить простое и понятное решение?
    Могу ли я представить свои данные каким-то другим способом, чтобы это позволило получить такое простое и понятное решение?

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

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

Ценность этого подхода в том, что вы иногда найдете удивительно хорошие решения. Элегантные решения. Решения стоят того времени, которое вы потратили на ответы на эти три вопроса. И вы не найдете эти решения, если вы просто войдете в следующую итерацию немедленно.

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

cmaster - восстановить монику
источник
Я мог бы пометить это как ответ, это имеет для меня наибольшее значение, и, похоже, существует консенсус относительно простой переоценки самой проблемы.
Джонатан
Также мне нравится, как вы разбили его на этапы, так что есть какая-то непростая процедура оценки ситуации.
Джонатан
OP, я вижу, что вы застряли в механике решения кода, так что да, вы "могли бы также отметить этот ответ". Лучший код, который мы написали, был после очень тщательного анализа требований, производных требований, диаграмм классов, взаимодействий классов и т. Д. Слава Богу, мы сопротивлялись всем чертовски дешевым местам (читай руководство и коллеги): «Вы мы тратим слишком много времени на дизайн »,« поторопись и получи код! »,« это слишком много классов! »и т. д. Как только мы добрались до этого, кодирование стало радостью - больше, чем забавой. Объективно это был лучший код во всем проекте.
радар Боб
13

Перво-наперво - шаблоны - это полезные абстракции, а не конец всего дизайна, не говоря уже о ОО-дизайне.

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

Теперь к мясу вещей:

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

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

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

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

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

Это приводит к потере мотивации, потому что «чистый» код иногда перестает быть опцией.

Идеальный враг хорошего. Получите что-то работающее, затем рефакторинг.

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

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

Telastyn
источник
Просто подумал, что должен сказать, что я исправил некоторые неверные интерпретации в редактировании. Я, как правило, делаю так, чтобы все получилось, и подход к рефакторингу. Тем не менее, часто это приводит к большому техническому долгу в моем опыте. Например, если я просто получаю что-то работающее, но потом сам или другие строят на нем верх, некоторые из этих вещей могут иметь неизбежные зависимости, которые также требуют тонны рефакторинга. Конечно, вы не всегда можете знать это заранее. Но если вы уже знаете, что этот подход несовершенен, нужно ли заставить его работать, а затем итеративно проводить рефакторинг, прежде чем строить код?
Джонатан
@ Джонатан - все дизайны представляют собой серию компромиссов. Да, вы должны выполнить итерацию немедленно, если дизайн имеет недостатки и у вас есть четкий способ улучшить его. Если нет явного улучшения, перестань трахаться.
Теластин
«они сократили свои обязанности таким образом, что это неестественно для рассматриваемой проблемы. Ни один хороший вариант не может быть запахом, что в вашем подходе есть что-то принципиально неправильное». Я бы поставил на это Некоторые разработчики никогда не сталкиваются с описанными проблемами, а другие, кажется, делают это довольно часто. Зачастую решение не так легко увидеть, когда его исходят от первоначального разработчика, потому что они уже смещают дизайн в том, как они решают проблему. Иногда единственный способ найти достаточно очевидное решение - это начать с требований дизайна.
Данк
8

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

Это все равно что пытаться построить машину, начиная с шины.

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

Как будет выглядеть модуль, если вы спроектируете и внедрите его с нуля? Насколько далеко от этого «идеала» ваша текущая реализация.

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

Питер Б
источник
4

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

Лучший совет, который я могу дать вам в этой ситуации:

  • не пытайтесь достичь своей главной цели одним "большим взрывом" ,

  • узнайте, как улучшить ваш код за несколько шагов!

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

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

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

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

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

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

Док Браун
источник
1
Это тоже очень полезно. Небольшой пошаговый рефакторинг или рефакторинг - хорошая идея, потому что тогда он становится чем-то, что можно постепенно завершать вместе с другой работой, не мешая ей слишком сильно. Я хотел бы одобрить несколько ответов!
Джонатан
1

Иногда лучшие два принципа дизайна - KISS * и YAGNI **. Не нужно втиснуть каждый известный шаблон дизайна в программу, которая просто должна напечатать «Hello world!».

 * Keep It Simple, Stupid
 ** You Ain't Gonna Need It

Отредактируйте после обновления вопроса (и до некоторой степени отражая то, что говорит Питер Б):

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

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

Саймон Б
источник
Я кое-что прояснил в редактировании, но в целом я имею в виду именно те проблемы, для которых не существует простого доступного решения.
Джонатан
Ах, хорошо, да, похоже, есть консенсус в отношении того, чтобы отступить и пересмотреть проблему. Это хорошая идея.
Джонатан
1

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

Еще я делаю, спрашиваю кого-то умнее меня. Это может принимать форму запроса в Stack Exchange, чтения статьи в Интернете на эту тему или запроса коллеги, имеющего больше опыта в этой области. Часто метод, который я считаю правильным, оказывается совершенно неверным для того, что я пытаюсь сделать. Я неверно охарактеризовал некоторые аспекты проблемы, и они на самом деле не соответствуют шаблону, который, я думаю, подходит. Когда это произойдет, то, что кто-то другой скажет: «Вы знаете, это больше похоже на…», может оказать большую помощь.

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

Так что TL; DR: Сделай перерыв, не заставляй его, попроси о помощи.

user1118321
источник