Как справиться с абстракцией и специализацией класса и его атрибутов? - PullRequest
0 голосов
/ 03 декабря 2018

Извиняюсь за это вполне аннотация заголовка.

Более четко:

  • У меня есть два класса Controler и Interface (аппаратный смысл, не связанныйдля проектирования шаблона)
  • Оба являются абстрактными с некоторыми чисто виртуальными методами и, следовательно, предназначены для подкласса
  • Мне нужно, чтобы каждый Controler был создан, чтобы быть связанным с Interface объектом
  • Каждый Controler подкласс работает только с подмножеством Interface подклассов (ControlerA + InterfaceA или ControlerB + InterfaceB, но не ControlerA + InterfaceB)
  • Каждый подкласс Interface имеет свои собственные методы, которые не наследуются (этопоэтому только один вид Controler может использовать его)
  • Базовый класс Controler должен вызвать некоторый метод базового класса Interface

Я пытаюсь передатьInterface объект для конструктора Controler, поэтому в моем определении класса атрибут Interface представляет абстрактный базовый класс.Но если моему Controler подклассу A необходимо вызвать определенный метод Interface A, возникает ошибка компиляции, поскольку базовый класс Interface не владеет этим методом.

Единственный обходной путь Iнайден был вызов dynamic_cast, но это, очевидно, кажется неправильным.


Вот мои Interface классы:

class Interface {
public:
  Interface() {};
  virtual void method() = 0;
};

class InterfaceA : public Interface {
public:
  InterfaceA() : Interface() {};
  void method() override { cout << "A overriding" << endl; }
  void onlyA() { cout << "A only" << endl; }
};

class InterfaceB : public Interface {
public:
  InterfaceB() : Interface() {};
  void method() override { cout << "B overriding" << endl; }
  void onlyB() { cout << "B only" << endl; }
};

Вот мои Controler классы:

class Controler {
public:
  Controler(Interface* i) : m_interface(i) {};
  virtual void uniqueMethod() = 0;
  void commonMethod() { m_interface->method(); }
  Interface* m_interface;
};

class ControlerA : public Controler {
public:
  ControlerA(InterfaceA* i) : Controler(i) {};
  void uniqueMethod() override {dynamic_cast<InterfaceA *>(m_interface)->onlyA();}
};

class ControlerB : public Controler {
public:
  ControlerB(InterfaceB* i) : Controler(i) {};
  void uniqueMethod() override {dynamic_cast<InterfaceB *>(m_interface)->onlyB();}
};

А вот как я планирую их использовать:

auto ia = new InterfaceA();
auto ca = ControlerA(ia);
ca.commonMethod();  // Method defined in the base class
ca.uniqueMethod();  // Method defined in InterfaceA only

Вы можете попробовать его на Repl.it .

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

1 Ответ

0 голосов
/ 03 декабря 2018

Проблема действительно есть.Существует инвариант между динамическим типом m_interface и динамическим типом объекта, который реализует Controler.Но этот инвариант не может поддерживаться классом Controler.Таким образом, элемент m_interface не является правильным местом.

Следствием этого является необходимость проверки во время выполнения, чтобы этот элемент имел правильный тип, используя dynamic_cast каждый раз, когда вы вызываете uniqueMethod.Если инвариант не работает, в коде будет UB, поскольку он будет разыменовывать нулевой указатель.

Так что на самом деле это не проблема шаблона проектирования, а, в сущности, рекомендация объектно-ориентированного программирования: классы должны обеспечиватьинварианты.

class Controler {
public:
  virtual void uniqueMethod() = 0;
  virtual void commonMethod() = 0;
};

class ControlerA : public Controler {
public:
  ControlerA(InterfaceA* i):m_interface{i} {
    assert(dynamic_cast<InterfaceA*>(i)!=nullptr);
    };
  void uniqueMethod() override { m_interface->onlyA();}
  void commonMethod() override { m_interface->method(); }
private: InterfaceA* m_interface;
};

class ControlerB : public Controler {
public:
  ControlerB(InterfaceB* i):m_interface{i} {
    assert(dynamic_cast<InterfaceB*>(i)!=nullptr);
    };
  void uniqueMethod() override { m_interface->onlyB();}
  void commonMethod() override { m_interface->method(); }
private: InterfaceB* m_interface;
};

Итак, похоже, у нас есть регулярный шаблон, поэтому мы можем подумать о более общем дизайне:

template<class Inter,void(Inter::* OnlyFunc)()>
class ControlerImpl : public Controler {
public:
  ControlerImpl(Inter* i):m_interface{i} {
    assert(dynamic_cast<Inter*>(i)!=nullptr);
    };
  void uniqueMethod() override { (m_interface->*OnlyFunc)();}
  void commonMethod() override { m_interface->method(); }
  private: Inter* m_interface;
};
using ControlerA = ControlerImpl<InterfaceA,&InterfaceA::onlyA>;
using ControlerB = ControlerImpl<InterfaceB,&InterfaceB::onlyB>;
...