Разработка на основе политики и лучшие практики - C ++ - PullRequest
24 голосов
/ 16 мая 2009
struct InkPen
{
  void Write()
  {
    this->WriteImplementation();
  }

  void WriteImplementation()
  {
    std::cout << "Writing using a inkpen" << std::endl;
  }

};

struct BoldPen
{
  void Write()
  {
    std::cout << "Writing using a boldpen" << std::endl;
  }
};

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

int main()
{
  Writer<InkPen> writer;
  writer.StartWriting();
  Writer<BoldPen> writer1;
  writer1.StartWriting();
  return 0;
}

Я написал приведенный выше код как часть изучения проектов на основе политики . У меня есть несколько вопросов по вышеуказанному коду

1 - Эта реализация выглядит правильно? Я имею в виду: действительно ли это похоже на дизайн на основе политики?

2 - Теперь я могу подключить любые ручки к писателю. Но что я буду делать, когда получу ручку без конструктора по умолчанию (только параметризованные конструкторы)? Как я справлюсь с этой ситуацией?

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

3 - когда вышеуказанный код используется как

Writer<InkPen> writer;

Я думаю, компилятор заменит PenType на InkPen . Если да, то почему я не могу вызвать просто Write () из StartWriting () вместо префикса имени базового класса ( PenType :: Write () ) ?

4 - Я думаю, что основанный на политике дизайн заставляет вас наследовать классы, которые семантически недопустимы. В приведенном выше коде писатель получен из пера только потому, что писатель использует перо. Но говорить, что писатель - ручка, семантически неверно. Есть ли другой лучший способ решить эту проблему или я что-то здесь упускаю?

Есть мысли?

Ответы [ 4 ]

24 голосов
/ 16 мая 2009

Вот как бы я реализовал класс:

template<class PenType>
class Writer
{
public:
  Writer(const PenType& pen = PenType()) : pen(pen) {}

  void StartWriting()
  {
    pen.Write();
  }

private:
  PenType pen;
};

Это позволяет пользователю передавать конкретный объект Pen в конструктор, если он не имеет конструктора по умолчанию или вы не хотите его использовать, и, во-вторых, он по-прежнему позволяет опустить PenType. объект, если вы счастливы позволить ему создать его с конструктором по умолчанию. Стандартная библиотека C ++ делает то же самое во многих классах (например, подумайте о распределителях для контейнерных классов).

Я удалил наследство. Похоже, он ничего не добавляет (и может вызвать проблемы. Возможно, вы не хотите, чтобы пользователь класса Writer напрямую вызывал функцию PenType :: Write. Вместо этого вы можете использовать частное наследование, но часто композиция является более простой и традиционный дизайн.

Как правило, разработка на основе политик не требует наследования. Добавление его в качестве члена работает так же хорошо. Если вы перейдете к наследованию, сделайте его частным, чтобы не было проблемы, которую вы упомянули как # 4.

12 голосов
/ 16 мая 2009

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

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

template<class FooPolicy, class BarPolicy>
class Baz {
    // implementation goes here
};

Затем вы используете методы из классов политики в вашем классе:

void Baz::someMethod(int someArg) {
    FooPolicy::methodInit();
    // some stuff
    BarPolicy::methodDone();
}

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

template<class FooPolicy, class BarPolicy>
class Baz {
  private:
    FooPolicy::State fooState; // might require 'typename' keyword, I didn't
                               // actually tried this in any compiler
    // rest of the Baz class
};

Объявление 2. Вы можете написать шаблон специализации - для конкретной комбинации основного класса и его политик вы можете написать специальную версию любого метода или конструктора, AFAIK:

template <>
Baz<SomeConcreteFooPolicy, SomeConcreteBazPolicy>::Baz(someArgument)
   : fooState(someArgument)
{
    // stuff here
}

Надеюсь, это вам немного поможет,

Mike

5 голосов
/ 29 июня 2010

1 - выглядит ли эта реализация правильный? Я имею в виду, это действительно выглядит как дизайн на основе политики?

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

2 - теперь я могу подключить любые ручки к писатель. Но что я буду делать, когда я получил ручка без конструктора по умолчанию (только параметризованные конструкторы)? Как я справлюсь с этой ситуацией?

Опять же, это странный пример класса политики. Однако, чтобы напрямую ответить на ваш вопрос, вы можете предоставить конструктор, который принимает PenType. Вам, вероятно, следует также избегать наследования от PenType и хранить его как член (не нужно тесно связывать ваш класс политики с его политиками).

Полагаю, компилятор заменит PenType с InkPen. Если да, то почему я не могу позвонить просто Write () из StartWriting () вместо префикса имя базового класса (PenType :: Write ())?

Когда вы наследуете от шаблона класса, вы должны указать this-> member или BaseClass :: member.

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

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

3 голосов
/ 17 июня 2013

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

Не используйте public наследование для разработки на основе политик! Это будет означать «есть-а» вместо «имеет-а» / «использует-а». Поэтому вы должны использовать private наследование!

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