BobDalgleish уже отметил, что этот (анти) паттерн называется « бродяга данных ».
По моему опыту, наиболее частой причиной избыточных данных является наличие множества связанных переменных состояния, которые действительно следует инкапсулировать в объект или структуру данных. Иногда может даже потребоваться вложить несколько объектов для правильной организации данных.
Для простого примера рассмотрим игру , которая имеет настраиваемый плеер характер, со свойствами , как playerName
, playerEyeColor
и так далее. Конечно, у игрока также есть физическая позиция на игровой карте и различные другие свойства, такие как, скажем, текущий и максимальный уровень здоровья и так далее.
На первой итерации такой игры вполне может быть разумным сделать все эти свойства глобальными переменными - в конце концов, есть только один игрок, и почти все в игре так или иначе связано с игроком. Таким образом, ваше глобальное состояние может содержать переменные, такие как:
playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100
Но в какой-то момент вы можете обнаружить, что вам нужно изменить этот дизайн, возможно, потому что вы хотите добавить многопользовательский режим в игру. В качестве первой попытки вы можете попытаться сделать все эти переменные локальными и передать их функциям, которые в них нуждаются. Однако вы можете обнаружить, что конкретное действие в вашей игре может включать цепочку вызовов функций, например:
mainGameLoop()
-> processInputEvent()
-> doPlayerAction()
-> movePlayer()
-> checkCollision()
-> interactWithNPC()
-> interactWithShopkeeper()
... и interactWithShopkeeper()
функция заставляет лавочника обращаться к игроку по имени, так что теперь вам внезапно необходимо передать все эти функции в playerName
виде бродягих данных . И, конечно же, если владелец магазина считает, что голубоглазые игроки наивны и будут взимать с них более высокие цены, вам придется пройти через всю цепочку функций и так далее.playerEyeColor
В данном случае правильным решением, конечно, является определение объекта игрока, который включает в себя имя, цвет глаз, положение, здоровье и любые другие свойства персонажа игрока. Таким образом, вам нужно только передать этот единственный объект всем функциям, которые так или иначе связаны с игроком.
Кроме того, некоторые из вышеперечисленных функций могут быть естественным образом превращены в методы этого объекта игрока, что автоматически даст им доступ к свойствам игрока. В некотором смысле, это просто синтаксический сахар, поскольку вызов метода объекта эффективно передает экземпляр объекта как скрытый параметр в метод, так или иначе, но это делает код более ясным и более естественным при правильном использовании.
Конечно, типичная игра имеет гораздо более «глобальное» состояние, чем просто игрок; например, у вас почти наверняка будет какая-то карта, на которой происходит игра, и список неигровых персонажей, перемещающихся по карте, и, возможно, предметы, размещенные на ней, и так далее. Вы также можете передавать все это как объекты-бродяги, но это опять-таки загромождает аргументы вашего метода.
Вместо этого решение состоит в том, чтобы объекты хранили ссылки на любые другие объекты, с которыми они имеют постоянные или временные отношения. Так, например, объект игрока (и, возможно, любые объекты NPC тоже), вероятно, должен хранить ссылку на объект «игровой мир», который будет иметь ссылку на текущий уровень / карту, так что методу, подобному методу player.moveTo(x, y)
, не нужно явно указывать карту в качестве параметра.
Точно так же, если бы наш персонаж игрока имел, скажем, собаку, которая следовала за ним, мы естественным образом сгруппировали бы все переменные состояния, описывающие собаку, в один объект, и дали бы объекту игрока ссылку на собаку (так, чтобы игрок мог скажем, назовите собаку по имени) и наоборот (чтобы собака знала, где находится игрок). И, конечно, мы, вероятно, хотели бы сделать объекты player и dog подклассами более общего объекта «актера», чтобы мы могли повторно использовать один и тот же код, скажем, для перемещения по карте.
Ps. Несмотря на то, что я использовал игру в качестве примера, есть и другие виды программ, в которых возникают такие проблемы. Однако, по моему опыту, основная проблема всегда одна и та же: у вас есть куча отдельных переменных (локальных или глобальных), которые действительно хотят объединить в один или несколько взаимосвязанных объектов. Независимо от того, «внедряют» ли ваши данные в ваши функции «глобальные» настройки параметров или кэшированные запросы к базе данных или векторы состояния в численном моделировании, решение неизменно состоит в том, чтобы идентифицировать естественный контекст , к которому относятся данные, и преобразовать его в объект. (или что-либо, что является самым близким эквивалентом на выбранном вами языке).