Ici, nous avons la classe de télévision et la classe DVD à titre d'exemple:
class TV
{
public:
TV();
virtual ~TV();
void on() const;
void off() const;
};
class DVDPlayer
{
public:
DVDPlayer();
~DVDPlayer();
void SetCD() const;
void StartPlay() const;
void Eject() const;
void PowerOff() const;
};
Nous créons un adaptateur qui aide les DVD à "prétend" TV:
class TVStyleAdapter :
public TV
{
public:
TVStyleAdapter(DVDPlayer* AdapteeDVD);
~TVStyleAdapter();
void on() const;
void off() const;
private:
DVDPlayer* DVD;
};
// override base class funcs:
void TVStyleAdapter::on() const
{
DVD->SetCD();
DVD->StartPlay();
}
void TVStyleAdapter::off() const
{
DVD->Eject();
DVD->PowerOff();
}
Après cela, je peux ajouter "virtuel" dans la classe TV (base) et remplacer les fonctions () OFF () dans la classe Adapter. Et cela fonctionnera correctement.
Mais les questions sont:
1. Oui, mais non :
Vous pouvez compiler n'importe quel code avec un TVStyleAdapter
au lieu d'un TV
, et cela fonctionnerait sans changement de TV
. Donc, dans un certain sens, la réponse pourrait être oui.
Mais cette réutilisation de l'interface de compilation fonctionnerait également si TVStyleAdapter
ne serait pas liée à TV
. Malheureusement, ce n'est pas dynamique: vous devez connaître le vrai type d'objet au moment de la compilation, et vous ne pouvez pas espérer un comportement dynamique et dépendant de l'objet au moment de l'exécution.
Si vous voulez un polymorphisme, c'est-à-dire un pointeur ou une référence à une classe de base de télévision et attendez-vous au bon comportement en fonction du type réel de l'objet que vous devez avoir le virtuel dans la classe de base.
2. Oui, mais qui a cassé le principe?
La question n'est pas tellement si elle doit changer la base de la classe de base, mais pourquoi la classe de base n'était pas déjà virtuelle.
Vous pensez que vous cassez le principe de près car vous devez ajouter le virtuel dans la classe de base. Mais en réalité, le concepteur de la classe de base a cassé le principe ouvert dans la première ligne.
. Conception pour le polymorphisme depuis le début
Si vous voulez qu'une classe soit polymorphe, cela se comporte différemment au Rutime en fonction de son type réel, vous devez la concevoir en conséquence du début. SO oui, virtuel à l'avance (et par conséquent un destructeur virtuel, même s'il reste vide)
Le motif de l'adaptateur classique, utilisant l'héritage, ne fonctionne tout simplement pas si la classe de base n'est pas préparée pour cela. Et oui, vous êtes correct, sans TV
ayant des méthodes virtuelles, elle n'est pas conforme à l'OCP. L'OCP nécessite des classes pour fournir des "points d'extension" ou des paramètres requis au préalable, voir Cet ancien Q & A sur l'OCP et ce que cela signifie pour la conception de classe.
En C++, toutefois, il est possible de mettre en œuvre le motif de l'adaptateur à l'aide de modèles, en faisant le type de classe de télévision, il utilise un paramètre de modèle:
template <class T> void myFunction(T tv)
{
tv->on();
}
Cela vous permettra d'écrire du code qui prend l'original TV
classe ou le TVStyleAdapter
comme paramètre, sans effectuer le on
et off
méthodes virtuelles. Notez que cela en fait une décision de compilation de la compilation que des deux classes sera utilisée, tandis que la solution d'héritage permettra de passer au temps d'exécution.
Si l'on a besoin d'une décision d'exécution avec la solution de modèle, la décision doit être prise par le code qui appelle soit myFunction<TV>
ou myFunction<TVStyleAdapter>
. Ou mieux utiliser la solution de Jackaidley, qui est un bon exemple de "composition sur l'héritage".
Oui, vous pouvez.
La solution est simple: envelopper TV
aussi et hériter à la fois de vos adaptateurs TV
et DVD
d'une classe de base abstraite commune. Vous pouvez ensuite utiliser cette classe de base abstraite à la place de partout où vous utilisiez la télévision et obtenez une flexibilité de temps d'exécution.
class TVBase {
public:
virtual ~TVBase() {}
virtual void on() const = 0;
virtual void off() const = 0;
};
class TVAdaptor : public TVBase {
public:
// Obviously you need a constructor!
virtual void on() const { tv->on(); }
virtual void off() const { tv->off(); }
private:
TV* tv;
};
class DVDAdaptor : public TVBase {
// Insert contents of your TV style adaptor here...
};
J'ai omis divers détails de mise en œuvre dans ce qui précède mais, espérons-le, vous pouvez voir ce que j'ai fait. Vous pouvez même l'implémenter comme un modèle de passage automatique pour on
et off
puis spécialisez pour les classes comme DVD
avec une interface différente.