Контейнерные отношения и инкапсуляция - PullRequest
2 голосов
/ 19 февраля 2010

В моем игровом движке у меня есть класс State, который представляет игровой мир в определенный момент времени. Это состояние содержит несколько объектов Body, каждый из которых определяет одно твердое тело. Каждое состояние имеет контейнер для отслеживания принадлежащих ему объектов Body, а каждое тело имеет указатель на свое родительское состояние.

План государственного класса:

class State {
private:
  std::set<Body*> _bodies;

public:
  //It could be done here
  void addBody(Body* body) {
    //Remove from old State
    if (body->_state)
      body->_state->_bodies.erase(this);

    //Set Body's back-reference
    body->_state = this;

    //Add to this State
    _bodies.insert(body);
  }
};

Схема класса тела:

class Body {
private:
  State* _state;

public:
  //It could also be done here
  void setState(State* state) {
    //Remove from old State
    if (_state)
      _state->_bodies.erase(this);

    //Set back-reference
    _state = state;

    //Add to the new State
    if (_state)
      _state->bodies.insert(this);
  }
};

Проблема, если таковая имеется, заключается в том, что добавление / удаление тела в / из государства требует смены частного члена каждого.

В какой-то момент я подумал о том, чтобы иметь статический класс State-Body-mediator, который бы имел доступ друзей к обоим классам. Звучит неплохо, потому что отношениями между государством и телом будет явно управлять это имя, но для управления простыми отношениями требуется много объявлений.

Есть ли "лучший" способ сделать это? Я хотел бы посмотреть, какие бывают идеи.

Ответы [ 3 ]

1 голос
/ 19 февраля 2010

Ну, один из способов сделать это - использовать функцию друга обоих классов:

void associate(State& s, Body& b)
{
    s.addBody(&b);
    b.setState(&s);
}
0 голосов
/ 21 февраля 2010

Цель private - создать класс one , который отвечает за поддержание связи тела с состоянием. Практический эффект заключается в том, что, когда связь идет не так, или нужно каким-то образом ее улучшить, у вас есть только один класс для проверки или изменения. Вы можете использовать друга, чтобы разделить ответственность, но вы можете создать головную боль для себя в долгосрочной перспективе. Я бы оставил один или оба класса «немыми».

// example 1: dumb state
void State::addBody(Body* body) { _bodies.insert(body); }
void State::removeBody(Body* body) { _bodies.erase(body); }
void Body::setState(State* state)
{
    if(_state) _state->removeBody(this);
    if(state) state->addBody(this);
    _state = state;
}

// example 2: dumb body
void Body::setState(State* state) { _state = state; }
State* Body::getState() const { return _state; }
void State::addBody(Body* body)
{
    if(body && body->getState() != this)
    {
        body->getState()->_bodies.erase(body);
        _bodies.insert(body);
        body->setState(this);
    }
}

// example 3: both are dumb
void State::addBody(Body* body) { _bodies.insert(body); }
void State::removeBody(Body* body) { _bodies.erase(body); }
void Body::setState(State* state) { _state = state; }
State* Body::getState() const { return _state; }
void BodyStateMachine::setBodyState(Body* body, State* state)
{
    if(body && body->getState() != state)
    {
        body->getState()->removeBody(body);
        state->addBody(body);
        body->setState(state);
    }
}

Выбранный вами метод должен зависеть от того, какой класс будет более публично использоваться (вы бы предпочли, чтобы другой код управлял «умным» классом). Вы можете защитить тупой класс (ы) дальше, создав интерфейс, который не предоставляет функции получения / установки.

0 голосов
/ 19 февраля 2010

На первом проходе я бы объединил два подхода:

void State::addBody(Body* body) {
    //Add to this State
    bodies_.insert(body); 

    //Remove from old State
    body->setState(this);
}

void State::removeBody(Body* body) {
    bodies_.erase(body); 
}


void Body::setState(State* state) {
    if(state_) {
        state_->removeBody(this);
    }
    state_ = state;
}

Это разъединяет внутренние органы государства и тела, так что вы можете, например, использовать какой-то совершенно иной механизм для списка государственных органов, и органу не нужно знать об этом. Вы должны быть осторожны с безопасностью исключений; этот пример преднамеренно помещает вставку контейнера в начало, поэтому, если это произойдет, состояние объектов не изменится, но я точно не знаю, что ничего другого здесь не может выбросить.

Обратите внимание, что я переместил подчеркивание к концам переменных-членов; Я знаю, что начальные подчеркивания - это общее соглашение, но на самом деле они зарезервированы для компилятора по стандарту C ++.

...