Поэтому, вероятно, как и многие, я часто сталкиваюсь с головными болями, связанными с проблемами проектирования, в которых, например, есть какой-то шаблон / подход к проектированию, который, кажется, интуитивно подходит к проблеме и имеет желаемые преимущества. Очень часто есть некоторая оговорка, которая затрудняет реализацию шаблона / подхода без какой-либо обработки, которая затем сводит на нет преимущества шаблона / подхода. Я могу очень легко закончить итерацией по многим шаблонам / подходам, потому что, как и ожидалось, почти все они имеют некоторые очень существенные предостережения в реальных ситуациях, в которых просто нет простого решения.
Пример:
Я приведу гипотетический пример, основанный на реальном, с которым я недавно столкнулся. Допустим, я хочу использовать композицию вместо наследования, потому что иерархии наследования препятствовали масштабируемости кода в прошлом. Я мог бы реорганизовать код, но затем обнаружил, что есть некоторые контексты, в которых суперклассу / базовому классу просто нужно вызывать функциональность на подклассе, несмотря на попытки избежать этого.
Следующий наилучший подход, по-видимому, заключается в реализации шаблона половинного делегата / наблюдателя и шаблона половинной композиции, чтобы суперкласс мог делегировать поведение или чтобы подкласс мог наблюдать события суперкласса. Тогда класс становится менее масштабируемым и обслуживаемым, потому что неясно, как его расширять, а также сложно расширять существующие слушатели / делегаты. Кроме того, информация не очень хорошо скрыта, потому что нужно знать реализацию, чтобы понять, как расширить суперкласс (если вы не используете комментарии очень широко).
Поэтому после этого я мог бы просто использовать наблюдателей или делегатов полностью, чтобы избежать недостатков, которые связаны с интенсивным смешением подходов. Однако это имеет свои проблемы. Например, я могу обнаружить, что в конечном итоге мне нужны наблюдатели или делегаты для увеличения количества поведений, пока мне не понадобятся наблюдатели / делегаты практически для каждого поведения. Одним из вариантов может быть просто иметь одного большого слушателя / делегата для всего поведения, но затем реализующий класс заканчивается множеством пустых методов и т. Д.
Тогда я мог бы попробовать другой подход, но с таким же количеством проблем. Затем следующий, и после и т. Д.
Этот итеративный процесс становится очень трудным, когда кажется, что каждый подход имеет столько же проблем, что и любой другой, и приводит к своего рода параличу проектных решений . Также трудно согласиться с тем, что код в конечном итоге будет одинаково проблематичным независимо от того, какой шаблон проектирования или подход используется. Если я окажусь в такой ситуации, значит ли это, что сама проблема нуждается в переосмыслении? Что делают другие, когда сталкиваются с такой ситуацией?
Изменить: Кажется, есть несколько толкований вопроса, которые я хочу прояснить:
- Я полностью исключил ООП, поскольку выясняется, что он на самом деле не является специфическим для ООП, плюс слишком легко неверно истолковать некоторые из комментариев, которые я сделал при прохождении по поводу ООП.
- Некоторые утверждают, что я должен использовать итеративный подход и пробовать разные шаблоны, или что мне следует отказаться от шаблона, когда он перестает работать. Это процесс, на который я собирался ссылаться в первую очередь. Я думал, что это было ясно из примера, но я мог бы сделать это более ясным, поэтому я отредактировал вопрос, чтобы сделать это.
Ответы:
Когда я принимаю такое жесткое решение, я обычно задаю себе три вопроса:
Каковы плюсы и минусы всех доступных решений?
Есть ли решение, которое я еще не рассматривал?
Самое главное:
каковы мои требования? Не требования на бумаге, настоящие фундаментальные?
Могу ли я как-то переформулировать проблему / настроить ее требования, чтобы она позволила получить простое и понятное решение?
Могу ли я представить свои данные каким-то другим способом, чтобы это позволило получить такое простое и понятное решение?
Это вопросы об основах воспринимаемой проблемы. Может оказаться, что вы на самом деле пытались решить не ту проблему. Ваша проблема может иметь особые требования, которые допускают гораздо более простое решение, чем общий случай. Вопрос вашей постановке вашей проблемы!
Я считаю очень важным тщательно продумать все три вопроса, прежде чем продолжить. Прогуляйтесь, прогуляйтесь по вашему офису, сделайте все возможное, чтобы действительно обдумать ответы на эти вопросы. Однако ответы на эти вопросы не должны занимать слишком много времени. Подходящее время может варьироваться от 15 минут до примерно недели, оно зависит от того, насколько плохими являются решения, которые вы уже нашли, и от их влияния в целом.
Ценность этого подхода в том, что вы иногда найдете удивительно хорошие решения. Элегантные решения. Решения стоят того времени, которое вы потратили на ответы на эти три вопроса. И вы не найдете эти решения, если вы просто войдете в следующую итерацию немедленно.
Конечно, иногда кажется, что хорошего решения не существует. В этом случае вы застряли с ответами на первый вопрос, а хорошее - это наименее плохое. В этом случае ценой того, что вы нашли время ответить на эти вопросы, является то, что вы, возможно, избегаете итераций, которые обречены на провал. Просто убедитесь, что вы вернетесь к кодированию в нужное время.
источник
Перво-наперво - шаблоны - это полезные абстракции, а не конец всего дизайна, не говоря уже о ОО-дизайне.
Во-вторых, современный ОО достаточно умен, чтобы знать, что не все является объектом. Иногда использование простых старых функций или даже некоторых скриптов с императивным стилем дает лучшее решение для определенных проблем.
Теперь к мясу вещей:
Почему? Когда у вас есть куча похожих вариантов, ваше решение должно быть проще! Вы не потеряете много, выбрав «неправильный» вариант. И действительно, код не исправлен. Попробуйте что-нибудь, посмотрите, хорошо ли это. Итерация .
Крепкие орешки. Сложные проблемы - математически сложные проблемы - просто тяжелые. Достаточно сложно Для них буквально нет хорошего решения. И оказывается, что простые проблемы не очень ценны.
Но будь осторожен. Слишком часто я видел людей, разочарованных отсутствием хороших вариантов, потому что они застряли, глядя на проблему определенным образом, или что они сократили свои обязанности таким образом, что это неестественно для рассматриваемой проблемы. «Нет хорошего варианта» может быть запахом, что с вашим подходом что-то принципиально не так.
Идеальный враг хорошего. Получите что-то работающее, затем рефакторинг.
Как я уже упоминал, итеративная разработка в целом сводит эту проблему к минимуму. Как только вы что-то заработаете, вы лучше познакомитесь с проблемой, которая поможет вам лучше ее решить. У вас есть реальный код для просмотра и оценки, а не какой-то абстрактный дизайн, который кажется неправильным.
источник
Ситуация, которую вы описываете, выглядит как восходящий подход. Вы берете лист, пытаетесь починить его и узнаете, что он связан с веткой, которая также связана с другой веткой и т. Д.
Это все равно что пытаться построить машину, начиная с шины.
Что вам нужно, это сделать шаг назад и посмотреть на картину в целом. Как этот лист находится в общем дизайне? Это все еще актуально и правильно?
Как будет выглядеть модуль, если вы спроектируете и внедрите его с нуля? Насколько далеко от этого «идеала» ваша текущая реализация.
Таким образом, у вас будет более полная картина того, к чему стремиться. (Или если вы решите, что это слишком много работы, какие проблемы).
источник
В вашем примере описывается ситуация, которая обычно возникает с большими кусками устаревшего кода и когда вы пытаетесь выполнить «слишком большой» рефакторинг.
Лучший совет, который я могу дать вам в этой ситуации:
не пытайтесь достичь своей главной цели одним "большим взрывом" ,
узнайте, как улучшить ваш код за несколько шагов!
Конечно, это легче написать, чем сделать, так как это сделать на самом деле? Что ж, вам нужна практика и опыт, это очень сильно зависит от конкретного случая, и нет строгого правила, говорящего «делай то или это», которое подходит для каждого случая. Но позвольте мне использовать ваш гипотетический пример. «Композиция по наследованию» - это не цель проектирования «все или ничего», это идеальный кандидат, которого нужно достичь за несколько небольших шагов.
Допустим, вы заметили, что «состав-наследование» является правильным инструментом для дела. Должны быть некоторые признаки того, что это разумная цель, иначе вы бы этого не выбрали. Итак, давайте предположим, что в суперклассе есть много функций, которые просто «вызываются» из подклассов, поэтому эта функциональность является кандидатом на то, чтобы не оставаться в этом суперклассе.
Если вы заметили, что не можете немедленно удалить суперкласс из подклассов, вы можете начать с рефакторинга суперкласса на более мелкие компоненты, инкапсулирующие функциональность, упомянутую выше. Начните с самых висящих фруктов, сначала извлеките несколько более простых компонентов, которые уже сделают ваш суперкласс менее сложным. Чем меньше становится суперкласс, тем легче будут дополнительные рефакторинги. Используйте эти компоненты из подклассов, а также из суперкласса.
Если вам повезет, оставшийся код в суперклассе станет настолько простым на протяжении всего этого процесса, что вы сможете удалить суперкласс из подклассов без каких-либо дополнительных проблем. Или вы заметите, что сохранение суперкласса больше не является проблемой, поскольку вы уже извлекли достаточное количество кода в компоненты, которые хотите повторно использовать без наследования.
Если вы не знаете, с чего начать, потому что вы не знаете, окажется ли рефакторинг простым, иногда лучший подход - это сделать рефакторинг с нуля .
Конечно, ваша реальная ситуация может быть более сложной. Так что учитесь, набирайтесь опыта и наберитесь терпения, чтобы сделать это правильно, потребуются годы. Здесь я могу порекомендовать две книги, может быть, вы найдете их полезными:
Рефакторинг Фаулера: описывает полный каталог очень маленьких рефакторингов.
Эффективная работа с унаследованным кодом от Feathers: дает отличный совет, как работать с большими кусками плохо спроектированного кода и сделать его более тестируемым за небольшие шаги
источник
Иногда лучшие два принципа дизайна - KISS * и YAGNI **. Не нужно втиснуть каждый известный шаблон дизайна в программу, которая просто должна напечатать «Hello world!».
Отредактируйте после обновления вопроса (и до некоторой степени отражая то, что говорит Питер Б):
Иногда вы рано принимаете архитектурное решение, которое приводит вас к определенному дизайну, который приводит к всевозможным уродствам, когда вы пытаетесь его реализовать. К сожалению, в этот момент «правильное» решение - сделать шаг назад и выяснить, как вы попали на эту позицию. Если вы еще не видите ответа, продолжайте отступать, пока не увидите.
Но если работа над этим была бы непропорциональной, нужно принять прагматичное решение, чтобы просто найти наименее уродливый способ решения проблемы.
источник
Когда я нахожусь в такой ситуации, первое, что я делаю, это останавливаюсь. Я переключаюсь на другую проблему и работаю над этим некоторое время. Может час, может день, может больше. Это не всегда вариант, но мое подсознание будет работать над вещами, в то время как мой сознательный мозг делает что-то более продуктивное. В конце концов, я возвращаюсь к нему со свежими глазами и пытаюсь снова.
Еще я делаю, спрашиваю кого-то умнее меня. Это может принимать форму запроса в Stack Exchange, чтения статьи в Интернете на эту тему или запроса коллеги, имеющего больше опыта в этой области. Часто метод, который я считаю правильным, оказывается совершенно неверным для того, что я пытаюсь сделать. Я неверно охарактеризовал некоторые аспекты проблемы, и они на самом деле не соответствуют шаблону, который, я думаю, подходит. Когда это произойдет, то, что кто-то другой скажет: «Вы знаете, это больше похоже на…», может оказать большую помощь.
С вышеизложенным связан дизайн или отладка по признанию. Вы идете к коллеге и говорите: «Я собираюсь рассказать вам о моей проблеме, а затем я собираюсь объяснить вам решения, которые у меня есть. Вы указываете на проблемы в каждом подходе и предлагаете другие подходы». «. Часто, прежде чем другой человек даже говорит, как я объясняю, я начинаю понимать, что один путь, который казался равным другим, на самом деле намного лучше или хуже, чем я первоначально думал. Получившийся разговор может либо усилить это восприятие, либо указать на новые вещи, о которых я не думал.
Так что TL; DR: Сделай перерыв, не заставляй его, попроси о помощи.
источник