Зазвичай це робиться за допомогою повідомлень. Ви можете знайти багато деталей в інших питаннях на цьому веб-сайті, як тут, чи там .
Щоб відповісти на ваш конкретний приклад, шлях - це визначити невеликий 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, якщо ви це зробите.