Как стать автором
Обновить

Комментарии 15

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

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

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

  2. Посмотреть на результат и сделать рефакторинг на основе реализации. То есть конечное выделение структур данных и объектов происходит на основе структуры программы, а не концепций реального мира.

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

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

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

Может, это чисто умозрительная проблема. А может, и нет, если взять массу классических алгоритмов "из книжки", там нередко самая разная магия происходит, которую трудно концептуально свести к сущностям, связям и протоколам общения.

Добавлю несколько мыслей (так как тема мне мне близка):

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

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

Приведу аналогию со зрением - мы просто открываем глаза и видим. Это происходит автоматически, не прилагая никаких усилий. Это создает иллюзию понятности данного процесса. Даже сейчас, если спросить неподготовленного программиста "как работает зрение?" или распознавание объектов, то он может придумать разные на первый взгляд правдоподобные модели для этого.

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

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

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

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

Было бы интересно услышать мнения большего количества людей.

Например я не уверен, что понимаю, как мой мозг строит модель реальности

Ну я на это не претендую. Мы говорим о модели физического мира (комнатах и коридорах), а как оно у нас в голове -- это отдельная история.

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

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

Было бы интересно услышать мнения большего количества людей.

Мне тоже :) Ну, ещё не вечер.

Мы говорим о модели физического мира (комнатах и коридорах), а как оно у нас в голове -- это отдельная история.

По моему мнению, ваша проблема с ООП подходом имеет прямое отношение к этому.

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

Имеются комнаты. Комнате соответствуют выходы. Выходы ведут в другие комнаты. Что не так с этим описанием? Почему на естественном языке всё в порядке, а в коде уже нет?

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

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

Надеюсь, я понятно сформулировал свою мысль.

Интересно, но попробуйте развить эту мысль. Может быть, она окажется тупиковой.

В игре есть не только комнаты/корридоры, но и состояние, игрок, команды, которые он отдаёт, игровой процесс.

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

Можно ли развивать игру, применив подход «корридоры первичны, комнаты вторичны?»

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

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

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

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

street = Room("Street", [])  # ok!
bedroom = Room("Bedroom", 
               [Exit("N", Room("Playroom", [Exit("S", bedroom)]))])  # ouch!

То есть в соответствии с данным кодом, получается что bedroom, который в Exit("S"), это не тот bedroom, который вы пытаетесь инициализировать. Как будто bedroom содержит сам себя, а не ссылку на себя. Что и приводит к противоречию.

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

Да, в целом согласен.

1) По поводу разделения "комнат" и "абстрактных комнат" -- верно, тут обманка бытового языка. "Выход ведёт из комнаты А в комнату Б" -- это неточная формулировка, потому что если её реализовывать ровно как сказано, то получится рекурсивная зависимость. Как разработчик я это понимаю, но в рамках упражнения мне было интересно аккуратно перевести русский на Python и посмотреть, что получится. А когда не получилось, стало интересно, почему не получилось.

2) По поводу "создать экземпляры, а потом соединить" -- это ровно та же история. Конечно, можно сделать комнату, а потом соединить комнаты коридорами. Но в описании на русском было не так.

Собственно, это и есть круг интересных вопросов. Мы понимаем, как написать программу, и мы понимаем, как её сформулировать на русском так, чтобы результат "склеился" хорошо ("создаём комнату А, создаём комнату Б, создаём коридор"). Интереснее обсудить, почему альтернативное описание (на первый взгляд ничем не уступающее) оказывается негодным.

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

Ладно, заканчиваю тут свои трактаты, а то я слишком много пишу.

Спасибо за поднятые вопросы.

То есть необходимости в введении абстракции в виде класса _RoomConcept на самом деле никакой нет
По поводу разделения «комнат» и «абстрактных комнат» — верно, тут обманка бытового языка. «Выход ведёт из комнаты А в комнату Б» — это неточная формулировка, потому что если её реализовывать ровно как сказано, то получится рекурсивная зависимость
Если посмотреть, как человек работает с этим описанием, то слова «ведёт в комнату Б» для него в описании комнаты не значимы, пока не возникнет задачи перейти в эту комнату или как-то ещё с ней взаимодействовать.

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

Поэтому RoomConcept на самом деле приближает нас к пониманию человеческого восприятия задачи.

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

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

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

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории