Не имеет значения, насколько тесно одна вещь связана с другой, если эта другая вещь никогда не меняется. Я обнаружил, что в течение многих лет было более продуктивно сосредоточиться на поиске меньшего количества причин для изменения вещей, на поиске стабильности, чем на том, чтобы облегчить их изменение, пытаясь достичь максимально возможной формы связи.
Развязка я нашел , чтобы быть очень полезными, до точки , где я иногда благоприятствую скромное дублирование кода разъединить пакеты. В качестве базового примера у меня был выбор использования моей математической библиотеки для реализации библиотеки изображений. Я не копировал некоторые базовые математические функции, которые было просто копировать.
Теперь моя библиотека изображений полностью независима от математической библиотеки, так что независимо от того, какие изменения я внесу в мою математическую библиотеку, это не повлияет на библиотеку изображений. Это ставит стабильность на первое место. Библиотека изображений теперь более стабильна, так как у нее значительно меньше причин для изменений, поскольку она отделена от любой другой библиотеки, которая может измениться (кроме стандартной библиотеки C, которая, надеюсь, никогда не изменится). В качестве бонуса, его также легко развернуть, когда он представляет собой отдельную библиотеку, которая не требует использования других библиотек для ее создания и использования.
Стабильность очень полезна для меня. Мне нравится создавать коллекцию хорошо протестированного кода, у которого есть все меньше и меньше причин когда-либо меняться в будущем. Это не несбыточная мечта; У меня есть C-код, который я использую и использую снова с конца 80-х, который с тех пор не изменился вообще. По общему признанию это низкоуровневые вещи, такие как пиксельно-ориентированный и связанный с геометрией код, в то время как многие мои высокоуровневые вещи устарели, но это то, что все еще помогает иметь много. Это почти всегда означает, что библиотека опирается на все меньше и меньше вещей, если вообще ничего внешнего. Надежность возрастает, если ваше программное обеспечение все в большей степени зависит от стабильных оснований, которые находят мало или не имеют причин для изменения. Меньше движущихся частей действительно хорошо, даже если на практике количество движущихся частей намного больше, чем у стабильных частей.
Слабая связь находится в том же духе, но я часто нахожу, что слабая связь намного менее устойчива, чем отсутствие связи. Если вы не работаете в команде с гораздо более выдающимися дизайнерами интерфейсов и клиентами, которые не меняют свое мнение, чем я когда-либо работал, даже чистые интерфейсы часто находят причины для изменения способами, которые все еще вызывают каскадные сбои во всем коде. Идея, что стабильность может быть достигнута путем направления зависимостей в сторону абстрактного, а не конкретного, полезна только в том случае, если дизайн интерфейса легче понять с первого раза, чем реализацию. Я часто нахожу это наоборот, когда разработчик мог создать очень хорошую, если не замечательную, реализацию, учитывая требования к дизайну, которые, по их мнению, они должны были выполнить, только чтобы обнаружить в будущем, что требования к дизайну полностью изменятся.
Поэтому мне нравится отдавать предпочтение стабильности и полной развязке, чтобы я мог, по крайней мере, с уверенностью сказать: «Эта маленькая изолированная библиотека, которую годами использовали и обеспечивала тщательное тестирование, почти не имеет вероятности требовать изменений независимо от того, что происходит в хаотическом внешнем мире. «. Это дает мне немного здравомыслия, независимо от того, какие изменения дизайна требуются снаружи.
Сцепление и стабильность, пример ECS
Мне также нравятся системы сущностей и компонентов, и они вводят много тесных связей, потому что зависимости системы от компонентов имеют прямой доступ к необработанным данным и манипулируют ими, например:
Все зависимости здесь довольно тесные, поскольку компоненты просто предоставляют необработанные данные. Зависимости не направляются в сторону абстракций, они направляются в необработанные данные, что означает, что каждая система имеет максимально возможный объем знаний о каждом типе компонента, к которому они обращаются, чтобы получить к нему доступ. Компоненты не имеют никакой функциональности, так как все системы обращаются к необработанным данным и вмешиваются в них. Тем не менее, очень легко рассуждать о такой системе, поскольку она такая плоская. Если текстура выходит из строя, то с помощью этой системы вы немедленно узнаете, что только компоненты системы рендеринга и рисования имеют доступ к компонентам текстуры, и вы, вероятно, можете быстро исключить систему рендеринга, поскольку она только концептуально читает из текстур.
Между тем слабосвязанная альтернатива может быть такой:
... со всеми зависимостями, направленными на абстрактные функции, а не на данные, и на каждую отдельную деталь в этой диаграмме, представляющую открытый интерфейс и его собственные функциональные возможности. Здесь все зависимости могут быть очень свободными. Объекты могут даже не зависеть напрямую друг от друга и взаимодействовать друг с другом через чистые интерфейсы. Тем не менее, очень трудно рассуждать об этой системе, особенно если что-то идет не так, учитывая сложную путаницу взаимодействий. Также будет больше взаимодействий (больше связей, хотя и слабее), чем в ECS, потому что сущности должны знать о компонентах, которые они объединяют, даже если они знают только об абстрактном общедоступном интерфейсе друг друга.
Кроме того, если что-то изменилось в дизайне, вы получите больше каскадных поломок, чем в ECS, и, как правило, будет больше причин и соблазнов для изменений в дизайне, поскольку каждая вещь пытается обеспечить хороший объектно-ориентированный интерфейс и абстракцию. Это сразу же приходит с идеей, что каждая мелочь будет пытаться наложить ограничения и ограничения на дизайн, и эти ограничения часто являются тем, что оправдывает изменения дизайна. Функциональность намного более ограничена и должна делать намного больше проектных предположений, чем необработанные данные.
На практике я обнаружил, что вышеупомянутый тип "плоской" системы ECS гораздо проще рассуждать, чем даже самые слабосвязанные системы со сложной паутиной слабых зависимостей, и, что наиболее важно для меня, я нахожу так мало причин для версии ECS когда-либо потребуется изменить какие-либо существующие компоненты, поскольку компоненты, от которых зависит, не несут никакой ответственности, кроме как предоставить соответствующие данные, необходимые для функционирования систем. Сравните сложность проектирования чистого IMotion
интерфейса и конкретного объекта движения, реализующего этот интерфейс, который предоставляет сложные функциональные возможности, пытаясь при этом сохранить инварианты над частными данными и компонент движения, который должен только предоставлять необработанные данные, относящиеся к решению проблемы, и не беспокоиться о функциональность.
Функциональность гораздо сложнее получить правильно, чем данные, поэтому я думаю, что зачастую предпочтительнее направлять поток зависимостей в сторону данных. В конце концов, сколько существует векторных / матричных библиотек? Сколько из них используют одно и то же представление данных и отличаются незначительно по функциональности? Бесчисленное множество, и все же у нас все еще так много, несмотря на идентичные представления данных, потому что мы хотим тонких различий в функциональности. Сколько библиотек изображений там? Сколько из них представляет пиксели различным и уникальным способом? Вряд ли, и еще раз показывает, что функциональность гораздо более нестабильны и склонны к изменениям дизайна, чем данные во многих сценариях. Конечно, в какой-то момент нам нужна функциональность, но вы можете проектировать системы, в которых большая часть зависимостей направляется к данным, а не в сторону абстракций или функциональности в целом. Это было бы приоритетом стабильности над сцеплением.
Самые стабильные функции, которые я когда-либо писал (те, которые я использовал и повторно использовал с конца 80-х годов без необходимости их изменения), были те, которые полагались на необработанные данные, как геометрическая функция, которая только что приняла массив числа с плавающей запятой и целые числа, а не те, которые зависят от сложного Mesh
объекта или IMesh
интерфейса, или умножение вектора / матрицы, которое просто зависело от, float[]
или double[]
не то, от которого зависело FancyMatrixObjectWhichWillRequireDesignChangesNextYearAndDeprecateWhatWeUse
.