Подтвердить что ты не робот

Политический дизайн и лучшие практики - С++

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 - Я думаю, что основанный на политике дизайн заставляет вас выводить из классов, которые семантически недействительны. В приведенном выше коде писатель получается из пера только потому, что писатель использует ручку. Но говоря, что писатель - это перо, оно семантически недействительно. Есть ли другой лучший способ решить эту проблему, или я здесь что-то не хватает?

Любые мысли?

4b9b3361

Ответ 1

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

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

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

private:
  PenType pen;
};

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

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

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

Ответ 2

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

Объявление 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

Ответ 3

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

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

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

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

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

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

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

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

Ответ 4

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

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