Это хорошая практика, чтобы использовать абстрактный класс внутренние классы в C ++ - PullRequest
2 голосов
/ 23 марта 2012

У меня есть класс, который должен поддерживать несколько (2 или 3 в данный момент) контекстов.Эти контексты не видны за пределами класса.В настоящее время у меня есть дизайн следующим образом.Является ли хорошей практикой использование абстрактного класса в качестве внутренних классов в C ++?Если у вас есть какие-либо альтернативы, пожалуйста, предложите.

В следующем примере: Проблема в том, что когда был написан оригинальный класс Car, методы (bar1 и bar2) были написаны в контексте Sedan.Сейчас я расширяю класс автомобилей, вводя новый тип автомобиля (хэтчбек).Алгоритмы в bar1 и bar2 написаны для седана и не могут быть использованы для хэтчбека.К сожалению, я не могу изменить существующий класс Car (т.е. сигнатуры конструктора или метода).Поэтому я представляю класс Type, как в примере выше.

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

class Car {
public:
   explicit Car(/* sedan-type */) {
      // set context as Sedan
   }

   explicit Car(/* hatchback-type */) {
      // set context as Hatchback
   }

   void bar1() { context_type.bar1() };
   void bar2() { context_type.bar2() };

private:
   class Type {
      virtual void bar1() = 0;
      virtual void bar2() = 0;
   }
   class SedanType {
      void bar1() {}
      void bar2() {}
   }
   class HatchBackType {      
      void bar1() {}
      void bar2() {}
   }
}

Ответы [ 2 ]

2 голосов
/ 23 марта 2012

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

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

class Base {
public:
  Base(SomeAbstractPolicy *policy);
  struct SomeAbstractPolicy {
    virtual ~SomeAbstractPolicy() {}
    virtual void stuff() = 0;
  };
};

Иногда ленивцы будут жаловаться на инверсию управления такими конструкциями. Размещение конкретных политик в одном заголовке или в качестве статических помощников в классе может быть разумным компромиссом.

class Base {
public:
  Base(SomeAbstractPolicy *policy);
  struct SomeAbstractPolicy {
    virtual ~SomeAbstractPolicy() {}
    virtual void stuff() = 0;
  };
  static SomeAbstractPolicy *CreateAwesomeConcretePolicy();
  static SomeAbstractPolicy *CreateSweetConcretePolicy();
};

Вы могли бы пойти дальше и использовать именованный конструктор.

class Base {
public:
  Base CreateAwesomeBase();
  Base CreateSweetBase();

private:
  struct SomeAbstractPolicy {
    virtual ~SomeAbstractPolicy() {}
    virtual void stuff() = 0;
  };

  Base(SomeAbstractPolicy *policy);
  SomeAbstractPolicy *policy;
};

Модульное тестирование - это намного сложнее, хотя оно того стоит.

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

0 голосов
/ 23 марта 2012

Почему бы не расширить класс Car и не переопределить методы bar1() и bar2(). В их реализациях вам не нужно вызывать методы вашего родительского класса, поэтому вы все равно получите то, что хотите.

Например, давайте возьмем седан:

class SedanCar : public Car {
  public :
     void bar1() {// do algorithm for sedan};
     void bar2() {// do algorithm for sedan};
}

Теперь любой экземпляр SedanCar с вызовами bar1() или bar2() будет выполнять реализации седана.

...