2012-01-27 4 views
0

У меня есть объект класса Level, который имеет несколько подсистем (дочерние объекты) использует для управления своим состоянием (например, EntityManager, InputManager, Physics). Я хочу разоблачить некоторые из этих подсистем во внешнем интерфейсе Level.Разоблачение методы ребенка объекта без дублирования

Вот одно решение:

uint32_t Level::CreateEntity() 
{ 
    return entityManager.CreateEntity(); 
} 

Entity& Level::GetEntity(uint32_t entityId) 
{ 
    return entityManager.GetEntity(entityId); 
} 

uint16_t Level::CreateInputState() 
{ 
    return inputManager.CreateInputState(); 
} 

void Level::AttachInputState(uint32_t entityId, uint16_t inputStateId) 
{ 
    inputManager.AttachInputState(entityId, inputStateId); 
} 

InputState& Level::GetInputState(uint16_t inputStateId) 
{ 
    return inputManager.GetInputState(inputStateId); 
} 

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

Другим решением является выявление подсистем в открытом интерфейсе. Предпочтительно этого можно избежать, поскольку не важно, чтобы объекты за пределами Level подсистемы вызывали пересылку.

Есть ли дизайн, который мог бы более элегантно справиться с этой проблемой?

+0

Вы хотите выставить все функции для детей или только некоторые специальные? – Nobody

+0

Почему объекты-менеджеры должны быть членами 'Level'? – wilhelmtell

+0

@Nobody Только некоторые специальные. – Kai

ответ

1

По просьбе OP I опубликует решение, которое я предложил в комментариях. Я уверен, что в новом стандарте C++ есть лучший шаблон с шаблонами, но в любом случае я опубликую решение препроцессора, которое является уродливым и не должно использоваться!

#define FUNCTION_DECLARATION(RETURNTYPE, FUNCTIONNAME, ...) \ 
    RETURNTYPE FUNCTIONNAME(__VA_ARGS__) 

Это может быть использовано в объявлении класса как:

class Level { 
    FUNCTION_DECLARATION(uint32_t, CreateEntity); 
    FUNCTION_DECLARATION(Entity&, GetEntity, uint32_t); 
}; 

определение будет выглядеть следующим образом:

#define FUNCTION_DEFINITION(RETURNTYPE, PROPERTY, FUNCTIONNAME, ...) \ 
    RETURNTYPE Level::FUNCTIONNAME(__VA_ARGS__) \ 
    { \ 
     return PROPERTY.FUNCTIONNAME(__VA_ARGS__); \ 
    } \ 

и теперь очень некрасиво использования, чтобы сделать эту работу:

FUNCTION_DEFINITION(Entity&, entityManager, GetEntity, uint32_t(entityId)) 

Я не могу гарантировать что это будет работать для любого типа, также я не тестировал большую часть кода. Как вы можете видеть, этот «взлом» с вводом будет работать только для простых типов, а не для ссылок или указателей. В классах он вызывает конструктор копирования!

Конечно, это можно улучшить, заменив VA_ARGS на переменную типа и variablename для каждого аргумента функции, но опять же это очень утомительно, и нужно было бы написать шаблон для каждого числа аргументов, которые должны использоваться. В результате результат будет иметь почти такой же код, как и раньше.

Так что позвольте мне снова заявить: придерживайтесь шаблонов, они намного лучше этого! Просто я не знаю, как это сделать с ними. Поэтому, пожалуйста, если есть кто-то, кто знает, как это сделать с помощью шаблонов, вместо того, чтобы бить меня за это ** код напишите красивое решение, для которого все здесь болят.

+0

Ах спасибо! Я не хотел, чтобы вы набрали полный макрокод, но, тем не менее, это полезно. – Kai

0

только думаю, я знаю, что бы сделать интерфейс более элегантным будет иметь другой объект содержит private LY EntityManager, Physics, и так далее, и он public ют разоблачить функции, которые вызывают каждую из функций, которые вы хотите клиентов чтобы иметь возможность вызвать базовые объекты. Make Level имеют экземпляр этих прокси-объектов в открытом интерфейсе и, возможно, делают эти прокси не скопируемыми, непередвижными и т. Д.

Это не уменьшит объем работы для вас (это фактически увеличило бы его), но это сделает интерфейс более организованным и более простым в использовании.

Пример:

class Physics { 
public: 
    T gravity() { ... } // we want them to be able to call this 

    T2 nope() { ... } // but not this 
}; 

class LevelPhysics { 
public: 
    T gravity() { return phys.gravity(); } 

private: 
    LevelPhysics(Physics& phys) : phys(phys) { } 

    Physics& phys; 

    friend class Level; 
}; 

class Level { 
public: 
    LevelPhysics GetPhysics() { return LevelPhysics(phys); } 

private: 
    Physics phys; 
}; 

Затем вы можете использовать его как

const LevelPhysics& phys = lvl.GetPhysics(); 

phys.gravity(); 
// but you can't use Physics::nope 

К сожалению, нет языка функция "сделать это для вас". Вы не можете обойтись, делая некоторые кодирования самостоятельно.

С другой стороны, если вы знаете, все классов, которые должны получить доступ к сокровенным членам Physics, вы можете просто сделать это:

class Physics { 
public: 
    T gravity() { ... } 

private: 
    T2 nope() { ... } 

    friend class Level; 
}; 

class Level { 
public: 
    Physics physics; 
}; 

Тогда

lvl.physics.gravity(); 
// but can't do lvl.physics.nope();, only Level can 
+0

Мне кажется, это усложняет интерфейс и дублирование кода. – Kai

+0

Как это усложняет интерфейс? Требование к одной строке большего количества кода в обмен на лучшую организацию, кажется, упрощает его для меня. И нет никакой возможности дублирования кода для этой задачи, если вы не хотите делать что-то, что я добавлю к своему ответу ... –

+0

@Kai там, посмотрите, позволяет ли ваш существующий код сделать это. –

1

Другой способ агрегирования функций - это частное наследование. Затем вы можете превратить некоторые унаследованные частные методы в общедоступные методы с помощью директивы using. Например:

#include <iostream> 

class feature_A 
{ 
public: 
    void func_A1() { std::cout << "A1" << std::endl; } 
    void func_A2() { std::cout << "A2" << std::endl; } 
}; 

class feature_B 
{ 
public: 
    void func_B1() { std::cout << "B1" << std::endl; } 
    void func_B2() { std::cout << "B2" << std::endl; } 
}; 

class compound : private feature_A, private feature_B 
{ 
public: 
    // Provide these functions as-is. 
    using feature_A::func_A1; 
    using feature_B::func_B1; 

    // Combine these two functions. 
    void func_C2() { func_A2(); func_B2(); } 
}; 

int main() 
{ 
    compound c; 
    c.func_A1(); 
    c.func_B1(); 
    c.func_C2(); 
    // c.func_A2(); // error: ‘void feature_A::func_A2()’ is inaccessible 
} 

Одно ограничение этих using деклараций является то, что они с именем. Если у вас несколько перегрузок одной и той же функции, вы не можете выбрать только одно, которое будет опубликовано. Аналогично для метода шаблона: вы не можете использовать using, чтобы выбрать только одну специализацию.

+0

Мне нравится это решение, хотя я думаю, что это может быть сложно, если один из классов «функций» использовал чисто виртуальные функции. – Kai

Смежные вопросы