2017-05-11 2 views
1

J'ai une classe Base et un grand nombre de classes dérivées (c'est-à-dire DerivedX) comme code de production. Sans toucher à ces classes, je crée une classe BaseExt dérivant de Base afin de manipuler des données internes à des fins de test.Lancer un cours de fratrie avec une bonne raison?

+-----------+   +-----------+ 
| Base  + <- - - - - | BaseExt | 
+-----------+   +-----------+ 
    /|\ 
    | 
+-----------+ 
| DerivedX + 
+-----------+ 

Voici l'exemple de code

 class Base { 
     public: 
     int data() const { return _data; } 
     protected: 
     Base() = default; 
     virtual ~Base() = default; 
     int _data; 
     }; 

     class Derived1 : public Base { 
     }; 

     class BaseExt : public Base { 
     public: 
     void inject_data(int data) { _data = data; } 
     }; 

Il fonctionne de manière intuitive pour moi.

std::shared_ptr<Base> p = std::make_shared<Derived1>(); 
    auto d1 = p->data(); // 0 
    std::static_pointer_cast<BaseExt, Base>(p)->inject_data(10); 
    auto d2 = p->data(); // 10 

de base: Je ne veux pas changer mon code de production (base et DerivedX)

Bien sûr, je pourrais étendre Derived1 pour faire le même travail, mais j'ai beaucoup de ces classes dérivées qui ajoutent trop de code pour une simple tâche.

la question est

  • est-il raisonnable de jeter dans la classe de frères et soeurs pour ce cas d'utilisation?
  • comment le rendre sûr? (Par exemple pas d'attribut dans la classe de frères et soeurs)
  • une solution meilleure et concise (sauf modification des classes de base et DereivedX)
+0

La façon plus sûre est de créer une fonction' friend' pour cela. – Zefick

+0

Pourquoi est-il intuitif, vous castez l'instance de type Derived1, à un autre type BaseExt. Et si BaseExt avait un membre de données? – marcinj

+0

@Zefick une fonction d'ami dans la classe de base? mais modifier les classes Base et DereivedX serait la dernière option – elgcom

Répondre

1

Qu'en est-il un modèle?

template<typename T> 
struct TestableDerived : T { 

    inject_data(int data) { 
     _data = data; 
    } 

    static std::shared_ptr<Base> createAndInjectData(int data) { 
     std::shared_ptr<TestableDerived<T>> ptr = std::make_shared<TestableDerived<T>>(); 
     ptr->inject_data(data); 
     return ptr; 
    } 
} 

Sinon, si vous êtes prêt à changer votre source de telle sorte que chaque classe dérivée hérite pratiquement de Base (c.-à-class DerivedX : public virtual Base), alors je pense que vous pouvez mélanger cela avec l'héritage multiple pour obtenir juste « une méthode supplémentaire au cours test »(je ne l'ai pas testé):

struct BaseExt : virtual Base { 
    void inject_data(int data) { _data = data; } 
} 

template<typename T> 
struct TestDerived<T> : virtual T, virtual BaseExt {} 

void doSomeTesting() { 
    std::shared_ptr<TestDerived<DerivedX>> p1 = std::make_shared<TestDerived<DerivedX>>(); 
    std::shared_ptr<DerivedX> p2 = p1; 
    std::shared_ptr<BaseExt> p3 = p1; 

    assert(p1->data()==0); 
    assert(p2->data()==0); 
    assert(p3->data()==0); 

    p3->inject_data(10); 

    assert(p1->data()==10); 
    assert(p2->data()==10); 
    assert(p3->data()==10); 
}