Я злоупотребляю политикой? - PullRequest
4 голосов
/ 01 апреля 2010

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

class DrawArrays {
protected:
  void sendDraw() const;
};

class DrawElements {
public:
  void setIndices( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
protected:
  void sendDraw() const;
};

template<class Policy>
class Vertices : public Policy {
  using Policy::sendDraw();
public:
  void render() const;
};

Когда политика выбирается во время выполнения, у меня есть разные варианты обхода ситуации.

Различные пути кода:

if(drawElements) {  
     Vertices<DrawElements> vertices;  
} else {   
     Vertices<DrawArrays> vertices;   
}

Наследование и виртуальные вызовы:

class PureVertices {
public:
  void render()=0;
};

template<class Policy>
class Vertices : public PureVertices, public Policy {
  //..
};

Оба решения кажутся мне неправильными. Первый создает бесполезный беспорядок, а второй - накладные расходы на виртуальные вызовы, которых я пытался избежать, используя политики в первую очередь.

Мне не хватает правильных решений или я использую неправильный шаблон для решения проблемы?

Ответы [ 5 ]

4 голосов
/ 01 апреля 2010

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

Помните: преждевременная оптимизация - корень всего зла!

0 голосов
/ 01 апреля 2010

Если вы не помещаете вызовы отрисовки в список отображения, тогда данные массива должны быть скопированы при его отрисовке. (Либо вызывающий блок блокируется до завершения работы графического процессора, либо драйвер копирует его из памяти приложения в безопасное место.) Таким образом, виртуальная функция не будет проблемой. И если вы помещаете их в список отображения, то виртуальная функция не будет проблемой, потому что она настраивается только один раз.

И в любом случае, ПК делают виртуальные звонки очень быстро. Они не бесплатны, это правда, но если рисовать (скажем) тысячи наборов вершин в кадре, то дополнительный виртуальный вызов функции за рисование вряд ли сломает банк. Из всех вещей, о которых нужно думать заранее, избегание использования виртуальной функции в самых разных ситуациях, для которых предназначены виртуальные функции, является, вероятно, одним из менее важных. Необязательно виртуальные функции стоит избегать; действительно полезные виртуальные функции невиновны, пока их вина не доказана ...

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

0 голосов
/ 01 апреля 2010

Почему вы против виртуальных звонков? Действительно ли накладные расходы для вас значительны? Я думаю, что код становится более читабельным, когда вы выражаете то, что вы хотите сделать, написав интерфейс и различные реализации вместо некоторых нечитаемых шаблонов.

В любом случае, почему вы наследуете Vertices от Policy класса? У вас уже есть это в качестве аргумента шаблона. Похоже, композиция здесь более уместна. Если вы используете наследование, вы можете иметь только один не шаблонный класс Vertices и изменять его поведение, передавая различные Policy объекты - это шаблон стратегии.

class Policy {
public:
    void sendDraw() const =0;
}

class Vertices {
public:
    Vertices(Policy * policy) :
    : policy(policy)
    {
    }

    void render() {
        // Do something with policy->sendDraw();
    }
}
0 голосов
/ 01 апреля 2010

В общем, если вам нужно, чтобы поведение изменялось во время выполнения, вам придется заплатить за это некоторые накладные расходы, будь то оператор switch / if или виртуальный вызов.Вопрос в том, сколько дисперсии времени выполнения вам нужно.Если вы уверены, что у вас будет только небольшое количество типов, тогда оператор switch может быть действительно уместным.Виртуальные вызовы дают большую гибкость для расширения в будущем, но вам не обязательно нужна такая гибкость;это зависит от проблемы.Тем не менее, есть еще много способов реализовать ваше «заявление о переключении» или «виртуальный вызов».Вместо переключателя /, если вы можете использовать Шаблон посетителя (более удобный для сопровождения), и вместо виртуальных вызовов вы можете использовать указатели на функции (когда для самого класса не имеет смысла указывать поведение, котороевызывается во время выполнения).Кроме того, хотя я не согласен со всем, что говорит автор (я думаю, что он искусственно делает свою идею и ООП взаимоисключающими), вас может заинтересовать Data Oriented Programming , особенно если вы работаете над рендерингом какваши имена классов предлагают.

0 голосов
/ 01 апреля 2010

Я не вижу ничего плохого в первом - он не выглядит для меня неразберихой, хотя здесь недостаточно кода, чтобы определить, может ли быть лучший рефакторинг.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...