Для меня это проблема сцепления и связана с гранулярностью дизайна. Даже самая слабая форма связи вводит зависимости от одного к другому. Если это делается для сотен или тысяч объектов, даже если все они относительно просты, придерживаются SRP, и даже если все зависимости направлены к стабильным абстракциям, это дает кодовую базу, которую очень трудно рассуждать как взаимосвязанное целое.
Есть практические вещи, которые помогают вам оценить сложность кодовой базы, не часто обсуждаемые в теоретической SE, например, насколько глубоко в стек вызовов вы можете попасть, прежде чем достигнуть конца, и насколько глубоко вам нужно пройти, прежде чем вы сможете, с большая уверенность, понять все возможные побочные эффекты, которые могут возникнуть на этом уровне стека вызовов, в том числе в случае исключения.
И я на собственном опыте обнаружил, что более плоские системы с более мелкими стеками вызовов гораздо легче рассуждать. В качестве крайнего примера можно привести систему объект-компонент, где компоненты - это просто необработанные данные. Функциональность есть только у систем, и в процессе реализации и использования ECS я обнаружил, что это самая простая из когда-либо существовавших систем, когда можно рассуждать о том, когда сложные кодовые базы, охватывающие сотни тысяч строк кода, в основном сводятся к нескольким десяткам систем, которые содержать все функции.
Слишком много вещей обеспечивают функциональность
Альтернативой ранее, когда я работал в предыдущих кодовых базах, была система с сотнями или тысячами в основном крошечных объектов, в отличие от нескольких десятков громоздких систем, в которых некоторые объекты использовались просто для передачи сообщений от одного объекта к другому (Message
объект, например, который имел собственный публичный интерфейс). Это в основном то, что вы получаете аналогично, когда вы возвращаете ECS обратно к точке, где компоненты имеют функциональные возможности, и каждая уникальная комбинация компонентов в сущности дает свой собственный тип объекта. И это, как правило, приводит к меньшим, более простым функциям, унаследованным и обеспеченным бесконечными комбинациями объектов, которые моделируют крошечные идеи ( Particle
объект противPhysics System
например). Тем не менее, он также имеет тенденцию создавать сложный график взаимозависимостей, который затрудняет рассуждение о том, что происходит на широком уровне, просто потому, что в базе кода есть так много вещей, которые действительно могут что-то сделать и, следовательно, могут сделать что-то не так - - типы, которые не являются типами «данных», но являются типами «объектов» со связанной функциональностью. Типы, которые служат чистыми данными без какой-либо связанной функциональности, не могут ошибаться, так как они ничего не могут сделать самостоятельно.
Чистые интерфейсы не очень помогают в этой проблеме понятности, потому что даже если это делает «зависимости времени компиляции» менее сложными и предоставляет больше возможностей для изменений и расширений, это не делает «зависимости времени выполнения» и взаимодействия менее сложными. Клиентский объект все еще заканчивается вызовом функций для конкретного объекта учетной записи, даже если они вызываются IAccount
. Полиморфизм и абстрактные интерфейсы имеют свое применение, но они не разделяют вещи таким образом, который действительно помогает вам размышлять обо всех побочных эффектах, которые могут произойти в любой данный момент. Чтобы добиться такого эффективного разделения, вам нужна кодовая база, в которой гораздо меньше функций, содержащих функциональность.
Больше данных, меньше функциональности
Таким образом, я нашел, что подход ECS, даже если вы не применяете его полностью, будет чрезвычайно полезным, поскольку он превращает то, что было бы сотнями объектов, в просто необработанные данные с помощью громоздких систем, более грубо спроектированных, которые обеспечивают все функциональность. Это максимизирует количество типов «данных» и сводит к минимуму количество типов «объектов», и, следовательно, абсолютно минимизирует количество мест в вашей системе, которые могут действительно пойти не так. Конечным результатом является очень «плоская» система без сложного графа зависимостей, только системы с компонентами, никогда не наоборот, и никогда не компоненты с другими компонентами. В основном это гораздо больше необработанных данных и намного меньше абстракций, что приводит к централизации и выравниванию функциональности базы кода для ключевых областей, ключевых абстракций.
30 более простых вещей не обязательно проще рассуждать о чем-то более сложном, если эти 30 более простых вещей взаимосвязаны, а сложная вещь стоит сама по себе. Таким образом, мое предложение состоит в том, чтобы на самом деле перенести сложность от взаимодействий между объектами и больше к более объемным объектам, которым не нужно взаимодействовать с чем-либо еще для достижения массовой развязки, к целым «системам» (не монолитам и объектам бога, учтите, и не классы с 200 методами, но что-то значительно более высокого уровня, чем a Message
или a, Particle
несмотря на минималистский интерфейс). И предпочитаю более простые старые типы данных. Чем больше вы зависите от них, тем меньше связи вы получите. Даже если это противоречит некоторым идеям SE, я обнаружил, что это очень помогает.