C ++ Как сделать внедрение зависимости от производного класса? - PullRequest
1 голос
/ 06 ноября 2019

У меня есть базовый класс B, полученный из абстрактного класса A. Идея иметь абстрактный класс A состоит в том, чтобы облегчить внедрение зависимостей и Mocking во время модульного тестирования. Так что А не имеют реализации. Почти как интерфейс C #. Все работает отлично.

Теперь у меня есть производный класс D, унаследованный от B. Теперь проблема в том, что D не имеет абстрактного класса для облегчения DI или Mocking. Вот небольшой код, который объясняет проблему:

class A // Abstract
{
public:
  virtual String Identify() = 0;
};

class B : public A
{
public:
  String Identify() {return "B"; }
};

class D : public B
{
public:
  String D_Work() {return "D_Work"; }
};

Итак, чтобы использовать B, я делаю A *b = new B();, что хорошо. Использующий класс не знает тип B. new () выполняется фабрикой или объект просто передается.

Но для использования D мне либо нужен фактический тип (который я пытаюсь получитьпрочь) или используйте тип A и приведите для вызова таких методов, как A *d = new D(); ((D*)d)->D_Work()) (для простоты используется c-стиль), что снова потребует от пользователя знать тип. Есть мысли по поводу дизайна?

Ответы [ 2 ]

1 голос
/ 06 ноября 2019

Вы можете добавить еще один абстрактный класс между B и D:

class B : public A {
public:
  string Identify() {return "B"; }
};

class C : public B {
public:
    virtual string D_Work() = 0;    
};

class D : public C {
public:
  string D_Work() {return "D_Work"; }
};

Смотрите, как это работает здесь: ideone


ЭтоПохоже, я неправильно понял, что вы хотите. Кажется, вы хотите продолжать использовать только указатель A. В этом случае я бы добавил виртуальную D_Work() функцию к A, которая выдает:

class A {
public:
  virtual string Identify() = 0;
  virtual string D_Work() { throw; } // If it's not defined, throw
};

Посмотрите, как она работает здесь: ideone

0 голосов
/ 06 ноября 2019

Другой подход заключается в том, что у вас есть несколько интерфейсов I, J, K и чистые методы в A, такие как GetI (), которые возвращают указатели на этот интерфейс.

Тогда все ваши классы реализации реализуют A, плюс любыедругие интерфейсы плюс любой из методов GetX ().

Ваш интерфейс - это A, I, J, K, и это все, что раскрывается. Это все, что нужно высмеивать.

В конечном счете, постоянное обновление A для добавления новых классов становится утомительным, поэтому вы вкладываете средства в другой механизм получения интерфейсов, вероятно, на основе токена, объявленного в интерфейсе. класс и метод шаблона в A, который разрешает токен и тип возврата, вызывая метод реализации, который делает то же самое. Или вы просто сдаетесь и пропускаете вызывающий номер dynamic_cast для интерфейсов.

Вскоре вы получите COM или что-то очень похожее.

class A;
{
  virtual String Identify() = 0;
  virtual I* GetI() { return nullptr;}
  virtual J* GetJ() { return nullptr;}
  virtual K* GetK() { return nullptr;}
};
class I
{
   virtual void Work() =0;
};
class J
{
   virtual void MoreWork() =0;
};

class B: A, I
{
  void Work() {}
  I* GetI() { return this;}
};
class C: A, J
{
  void MoreWork() {}
  I* GetJ() { return this;}
};
...