Зазвичай це робиться за допомогою повідомлень. Ви можете знайти багато деталей в інших питаннях на цьому веб-сайті, як тут, чи там .
Щоб відповісти на ваш конкретний приклад, шлях - це визначити невеликий Message
клас, який ваші об'єкти можуть обробляти, наприклад:
struct Message
{
Message(const Objt& sender, const std::string& msg)
: m_sender(&sender)
, m_msg(msg) {}
const Obj* m_sender;
std::string m_msg;
};
void Obj::Process(const Message& msg)
{
for (int i=0; i<m_components.size(); ++i)
{
// let components do some stuff with msg
m_components[i].Process(msg);
}
}
Таким чином, ви не «забруднюєте» свій Obj
клас інтерфейсу методами, що стосуються компонентів. Деякі компоненти можуть вибрати обробку повідомлення, деякі можуть просто проігнорувати його.
Ви можете почати, зателефонувавши цьому методу безпосередньо з іншого об’єкта:
Message msg(obj1, "EmitForce(5.0,0.0,0.0)");
obj2.ProcessMessage(msg);
У цьому випадку, obj2
's Physics
вибере повідомлення і зробить будь-яку обробку, яку він повинен зробити. Після закінчення він буде:
- Надішліть повідомлення "SetPosition" самому, що
Position
компонент вибере;
- Або безпосередньо звертайтеся до
Position
компонента для модифікацій (зовсім неправильно для чистого дизайну на основі компонентів, оскільки ви не можете припустити, що кожен об'єкт має Position
компонент, але Position
компонент може бути вимогою Physics
).
Як правило, добре затримати фактичну обробку повідомлення до оновлення наступного компонента. Обробка його негайно може означати надсилання повідомлень іншим компонентам інших об'єктів, тому надсилання лише одного повідомлення може швидко означати нерозривний стек спагетті.
Пізніше вам, мабуть, доведеться піти на більш вдосконалену систему: асинхронні черги повідомлень, надсилання повідомлень групі об'єктів, реєстрація / відреєстрація за допомогою компонентів та ін.
Message
Клас може бути універсальним контейнером для простої рядки , як показано вище, але обробка рядків під час виконання не надто ефективні. Ви можете знайти контейнер із загальними значеннями: рядки, цілі числа, поплавці ... Ім'я або ще краще - ідентифікатор, щоб розрізняти різні типи повідомлень. Або ви також можете отримати базовий клас відповідно до конкретних потреб. У вашому випадку ви можете уявити собі, EmitForceMessage
що походить від Message
і додає бажаний вектор сили - але остерігайтеся витрат на виконання RTTI, якщо ви це зробите.