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)
не потрібно чітко дана карта як параметр.
Так само, якби у нашого персонажа гравця був, скажімо, домашня собака, яка стежила за ними навколо, ми, природно, групували б усі змінні стану, що описують собаку, в один об'єкт, і дамо гравцеві об'єкт посилання на собаку (щоб гравець міг , скажімо, називайте собаку по імені) і навпаки (щоб собака знала, де знаходиться гравець). І, звичайно, ми, мабуть, хотіли б зробити гравця та собаку об'єктами обох підкласів більш загального об'єкта "актор", щоб ми могли повторно використовувати один і той же код для, скажімо, переміщення обох по карті.
Пс. Незважаючи на те, що я використовував гру як приклад, є й інші види програм, де такі проблеми також виникають. На мій досвід, однак, основна проблема має тенденцію завжди бути однаковою: у вас є купа окремих змінних (будь то локальних чи глобальних), які дійсно хочуть об'єднатись в один або кілька об'єднаних між собою об'єктів. Незалежно від того, що "дані бродячого", що вступають у ваші функції, складаються з "глобальних" параметрів параметрів або кешованих запитів баз даних або векторів стану в чисельному моделюванні, рішення незмінно може визначити природний контекст, якому належать дані, і зробити це об'єктом (або все, що є найближчим еквівалентом у вибраній вами мові).