Alexis Guillaume writes:
>> Il y a le pattern "chaine de responsabilité" que tu peux
>> employer. Ca a l'air d'en être une forme (je ne peux pas
>> dire avec les éléments que tu donnes).
> En effet je me renseigne sur ce pattern et ça a l'air d'être
> ce que je veux faire. Merci beaucoup ! :-)
Oui, enfin, c'est le fonctionnement normal en programmation
objet quand on surcharge une méthode, d'appeler la méthode de
la superclass.
C'est tellement normal, que dans les langages de programmation
objet normaux, il y a en général un mot clé ou une syntaxe
particulière, quand ce n'est pas automatique.
Par exemple, en Objective-C:
-doSomething
{
[self doSomethingBefore];
[super doSomething]; // ***
[self doSomethingAfter];
}
en Ruby:
def doSomething
doSomethingBefore
super.doSomething
doSomethingAfter
end
ou en Common Lisp:
(defmethod do-something ((self my-class))
(do-something-before)
(call-next-method) ;; ***
(do-something-after))
ou encore:
(defmethod do-something :before ((self my-class))
(do-something-before))
;; La méthode do-something est appelée automatiquement.
(defmethod do-something :after ((self my-class))
(do-something-after))
Tu es en train de documenter quelque chose qui est ce qui
devrait se faire par défaut, car c'est la façon naturelle
d'implémenter le principe de substitution de Lyskov.
Alors la question, c'est si on doit appeler cette méthode dans
la superclasse simplement pour assurer que la post-condition
est bien remplie (étant donnée la pré-condition), ou s'il y a
une autre raison (par exemple, un effet de bord particulier
que l'on veut obtenir.
Dans le premier cas, on n'a rien besoin de documenter car ça
doit être toujours le cas, et il n'y a pas de raison pour
qu'une sous-classe ne puisse pas s'assurer de la
post-condition par d'autres voies que la superclasse (même si
en général, un bon programmeur étant faignant essayera plutôt
d'appeler la super classe que de réinventer la roue).
Dans le second cas, il vaudrait mieux avoir une méthode Y
distincte dans la super classe, et documenter simplement que
toute surcharge de la méthode originale X doit s'assurer que
cette méthode Y est appelée, que ce soit directement ou via la
méthode X de la superclasse.
Alexis Guillaume <a...@free.fr> writes:
>> Il y a le pattern "chaine de responsabilité" que tu peux
>> employer. Ca a l'air d'en être une forme (je ne peux pas
>> dire avec les éléments que tu donnes).
> En effet je me renseigne sur ce pattern et ça a l'air d'être
> ce que je veux faire. Merci beaucoup ! :-)
Oui, enfin, c'est le fonctionnement normal en programmation
objet quand on surcharge une méthode, d'appeler la méthode de
la superclass.
C'est tellement normal, que dans les langages de programmation
objet normaux, il y a en général un mot clé ou une syntaxe
particulière, quand ce n'est pas automatique.
Par exemple, en Objective-C:
-doSomething
{
[self doSomethingBefore];
[super doSomething]; // ***
[self doSomethingAfter];
}
en Ruby:
def doSomething
doSomethingBefore
super.doSomething
doSomethingAfter
end
ou en Common Lisp:
(defmethod do-something ((self my-class))
(do-something-before)
(call-next-method) ;; ***
(do-something-after))
ou encore:
(defmethod do-something :before ((self my-class))
(do-something-before))
;; La méthode do-something est appelée automatiquement.
(defmethod do-something :after ((self my-class))
(do-something-after))
Tu es en train de documenter quelque chose qui est ce qui
devrait se faire par défaut, car c'est la façon naturelle
d'implémenter le principe de substitution de Lyskov.
Alors la question, c'est si on doit appeler cette méthode dans
la superclasse simplement pour assurer que la post-condition
est bien remplie (étant donnée la pré-condition), ou s'il y a
une autre raison (par exemple, un effet de bord particulier
que l'on veut obtenir.
Dans le premier cas, on n'a rien besoin de documenter car ça
doit être toujours le cas, et il n'y a pas de raison pour
qu'une sous-classe ne puisse pas s'assurer de la
post-condition par d'autres voies que la superclasse (même si
en général, un bon programmeur étant faignant essayera plutôt
d'appeler la super classe que de réinventer la roue).
Dans le second cas, il vaudrait mieux avoir une méthode Y
distincte dans la super classe, et documenter simplement que
toute surcharge de la méthode originale X doit s'assurer que
cette méthode Y est appelée, que ce soit directement ou via la
méthode X de la superclasse.
Alexis Guillaume writes:
>> Il y a le pattern "chaine de responsabilité" que tu peux
>> employer. Ca a l'air d'en être une forme (je ne peux pas
>> dire avec les éléments que tu donnes).
> En effet je me renseigne sur ce pattern et ça a l'air d'être
> ce que je veux faire. Merci beaucoup ! :-)
Oui, enfin, c'est le fonctionnement normal en programmation
objet quand on surcharge une méthode, d'appeler la méthode de
la superclass.
C'est tellement normal, que dans les langages de programmation
objet normaux, il y a en général un mot clé ou une syntaxe
particulière, quand ce n'est pas automatique.
Par exemple, en Objective-C:
-doSomething
{
[self doSomethingBefore];
[super doSomething]; // ***
[self doSomethingAfter];
}
en Ruby:
def doSomething
doSomethingBefore
super.doSomething
doSomethingAfter
end
ou en Common Lisp:
(defmethod do-something ((self my-class))
(do-something-before)
(call-next-method) ;; ***
(do-something-after))
ou encore:
(defmethod do-something :before ((self my-class))
(do-something-before))
;; La méthode do-something est appelée automatiquement.
(defmethod do-something :after ((self my-class))
(do-something-after))
Tu es en train de documenter quelque chose qui est ce qui
devrait se faire par défaut, car c'est la façon naturelle
d'implémenter le principe de substitution de Lyskov.
Alors la question, c'est si on doit appeler cette méthode dans
la superclasse simplement pour assurer que la post-condition
est bien remplie (étant donnée la pré-condition), ou s'il y a
une autre raison (par exemple, un effet de bord particulier
que l'on veut obtenir.
Dans le premier cas, on n'a rien besoin de documenter car ça
doit être toujours le cas, et il n'y a pas de raison pour
qu'une sous-classe ne puisse pas s'assurer de la
post-condition par d'autres voies que la superclasse (même si
en général, un bon programmeur étant faignant essayera plutôt
d'appeler la super classe que de réinventer la roue).
Dans le second cas, il vaudrait mieux avoir une méthode Y
distincte dans la super classe, et documenter simplement que
toute surcharge de la méthode originale X doit s'assurer que
cette méthode Y est appelée, que ce soit directement ou via la
méthode X de la superclasse.
On Wed, 04 Mar 2009 18:12:36 +0100, Wykaaa :
>> Ben oui, mais justement, C++ n'est pas un langage objet, et
>> n'a d'ailleurs pas la notion de "méthode".
>Ah bon ? et une fonction membre c'est quoi ?
Une fonction membre.
Le mot "méthode" est généralement employé dans les langages où
les fonctions membres sont toutes virtuelles.
En C++, une fonction membre n'est pas virtuelle, sauf si on le
demande explicitement. Du coup, utiliser le mot "méthode"
n'apporte qu'une inutile confusion : signifie-t-il "fonction
membre" ou "fonction membre virtuelle" ?
On Wed, 04 Mar 2009 18:12:36 +0100, Wykaaa <wyk...@yahoo.fr>:
>> Ben oui, mais justement, C++ n'est pas un langage objet, et
>> n'a d'ailleurs pas la notion de "méthode".
>Ah bon ? et une fonction membre c'est quoi ?
Une fonction membre.
Le mot "méthode" est généralement employé dans les langages où
les fonctions membres sont toutes virtuelles.
En C++, une fonction membre n'est pas virtuelle, sauf si on le
demande explicitement. Du coup, utiliser le mot "méthode"
n'apporte qu'une inutile confusion : signifie-t-il "fonction
membre" ou "fonction membre virtuelle" ?
On Wed, 04 Mar 2009 18:12:36 +0100, Wykaaa :
>> Ben oui, mais justement, C++ n'est pas un langage objet, et
>> n'a d'ailleurs pas la notion de "méthode".
>Ah bon ? et une fonction membre c'est quoi ?
Une fonction membre.
Le mot "méthode" est généralement employé dans les langages où
les fonctions membres sont toutes virtuelles.
En C++, une fonction membre n'est pas virtuelle, sauf si on le
demande explicitement. Du coup, utiliser le mot "méthode"
n'apporte qu'une inutile confusion : signifie-t-il "fonction
membre" ou "fonction membre virtuelle" ?
(Pascal J. Bourguignon) writes:
>> Une fonction virtuelle pure a peut avoir un corps bien que
>> ce soit assez rare (sauf peut être quand c'est le
>> destructeur).
> Ça doit être "nouveau". Quelle est la syntaxe pour l'écrire?
C'est déjà dans la deuxième édition de TC++PL, en 1991. J'ai
pas cherché dans des documents antérieurs.
p...@informatimago.com (Pascal J. Bourguignon) writes:
>> Une fonction virtuelle pure a peut avoir un corps bien que
>> ce soit assez rare (sauf peut être quand c'est le
>> destructeur).
> Ça doit être "nouveau". Quelle est la syntaxe pour l'écrire?
C'est déjà dans la deuxième édition de TC++PL, en 1991. J'ai
pas cherché dans des documents antérieurs.
(Pascal J. Bourguignon) writes:
>> Une fonction virtuelle pure a peut avoir un corps bien que
>> ce soit assez rare (sauf peut être quand c'est le
>> destructeur).
> Ça doit être "nouveau". Quelle est la syntaxe pour l'écrire?
C'est déjà dans la deuxième édition de TC++PL, en 1991. J'ai
pas cherché dans des documents antérieurs.
Jean-Marc Bourguet writes:
> (Pascal J. Bourguignon) writes:
>> Alexis Guillaume writes:
>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).
>> > En effet je me renseigne sur ce pattern et ça a l'air
>> > d'être ce que je veux faire. Merci beaucoup ! :-)
>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode
>> de la superclass.
> Bizarre, dans la plupart des cas, ce n'est pas ce que je
> fais (a commencer par tous les cas ou la fonction membre est
> pure dans la classe de base).
Oui, les méthodes virtuelles pures sont pratiques pour
permettre au compilateur de vérifier qu'on a bien fourni une
implémentation dans les sous-classes. Cependant ce n'est pas
adapté à un style de programmation plus dynamique, et ça donne
plus de travail à l'utilisateur de la classe abstraite.
En général je préfère définir une implémentation par défaut
pour toutes mes méthodes, même dans les classes "abstraites".
Jean-Marc Bourguet <j...@bourguet.org> writes:
> p...@informatimago.com (Pascal J. Bourguignon) writes:
>> Alexis Guillaume <a...@free.fr> writes:
>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).
>> > En effet je me renseigne sur ce pattern et ça a l'air
>> > d'être ce que je veux faire. Merci beaucoup ! :-)
>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode
>> de la superclass.
> Bizarre, dans la plupart des cas, ce n'est pas ce que je
> fais (a commencer par tous les cas ou la fonction membre est
> pure dans la classe de base).
Oui, les méthodes virtuelles pures sont pratiques pour
permettre au compilateur de vérifier qu'on a bien fourni une
implémentation dans les sous-classes. Cependant ce n'est pas
adapté à un style de programmation plus dynamique, et ça donne
plus de travail à l'utilisateur de la classe abstraite.
En général je préfère définir une implémentation par défaut
pour toutes mes méthodes, même dans les classes "abstraites".
Jean-Marc Bourguet writes:
> (Pascal J. Bourguignon) writes:
>> Alexis Guillaume writes:
>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).
>> > En effet je me renseigne sur ce pattern et ça a l'air
>> > d'être ce que je veux faire. Merci beaucoup ! :-)
>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode
>> de la superclass.
> Bizarre, dans la plupart des cas, ce n'est pas ce que je
> fais (a commencer par tous les cas ou la fonction membre est
> pure dans la classe de base).
Oui, les méthodes virtuelles pures sont pratiques pour
permettre au compilateur de vérifier qu'on a bien fourni une
implémentation dans les sous-classes. Cependant ce n'est pas
adapté à un style de programmation plus dynamique, et ça donne
plus de travail à l'utilisateur de la classe abstraite.
En général je préfère définir une implémentation par défaut
pour toutes mes méthodes, même dans les classes "abstraites".
Michael DOUBEZ writes:Pascal J. Bourguignon wrote:Alexis Guillaume writes:Il y a le pattern "chaine de responsabilité" que tu peux employer. Ca a
l'air d'en être une forme (je ne peux pas dire avec les éléments que tu
donnes).
En effet je me renseigne sur ce pattern et ça a l'air d'être ce que je
veux faire. Merci beaucoup ! :-)
Oui, enfin, c'est le fonctionnement normal en programmation objet
quand on surcharge une méthode, d'appeler la méthode de la superclass.
C'est tellement normal, que dans les langages de programmation objet
normaux, il y a en général un mot clé ou une syntaxe particulière,
quand ce n'est pas automatique.
Ce qui ne marche que dans le cas où l'héritage est simple. Comme C++
supporte l'héritage multiple, c'est assez difficile de fournir un tel
mot clé.
Tu n'as pas bien fait attention aux exemples Common Lisp.
(call-next-method) appelle la méthode suivante, en tenant compte de
l'héritage multiple. Ce n'est pas forcément celle d'une super-classe.
Mais je n'entrerai pas plus dans la richesse du système objet de
Common Lisp.
Une autre solution serait d'utiliser une méthode crochet, c'est à dire
qu'on défini dans la super classe une méthode WX qui s'assure que la
méthode Y est appelée en même temps que X, la superclasse ne
définissant pas X (ou fournissant une définition par défaut vide), en
spécifiant simplement qu'une sous-classe doit surcharger X (comme elle
le veut, la méthode Y étant appelée automatiquemetn par WX.
Le problème ici est qu'il peut y avoir un nombre indéfini de surcharge
de la classe et chaque surcharge intermédiaire doit être appelée.
Bien entendu, toutes ces méthodes sont virtuelles, et quand la super
classe envoit le message this->X(), c'est la méthode de la sous-classe
dont this est exactement l'instance qui est appelée. Tout ce qu'on lui
demande c'est d'assurer la post-condition. Le mieux pour elle c'est
d'appeler la méthode X de la superclasse mais si elle ne le fait pas
ce n'est pas grave, et de toutes façons WX s'assure que Y soit
appelée.
Michael DOUBEZ <michael.doubez@free.fr> writes:
Pascal J. Bourguignon wrote:
Alexis Guillaume <alek@free.fr> writes:
Il y a le pattern "chaine de responsabilité" que tu peux employer. Ca a
l'air d'en être une forme (je ne peux pas dire avec les éléments que tu
donnes).
En effet je me renseigne sur ce pattern et ça a l'air d'être ce que je
veux faire. Merci beaucoup ! :-)
Oui, enfin, c'est le fonctionnement normal en programmation objet
quand on surcharge une méthode, d'appeler la méthode de la superclass.
C'est tellement normal, que dans les langages de programmation objet
normaux, il y a en général un mot clé ou une syntaxe particulière,
quand ce n'est pas automatique.
Ce qui ne marche que dans le cas où l'héritage est simple. Comme C++
supporte l'héritage multiple, c'est assez difficile de fournir un tel
mot clé.
Tu n'as pas bien fait attention aux exemples Common Lisp.
(call-next-method) appelle la méthode suivante, en tenant compte de
l'héritage multiple. Ce n'est pas forcément celle d'une super-classe.
Mais je n'entrerai pas plus dans la richesse du système objet de
Common Lisp.
Une autre solution serait d'utiliser une méthode crochet, c'est à dire
qu'on défini dans la super classe une méthode WX qui s'assure que la
méthode Y est appelée en même temps que X, la superclasse ne
définissant pas X (ou fournissant une définition par défaut vide), en
spécifiant simplement qu'une sous-classe doit surcharger X (comme elle
le veut, la méthode Y étant appelée automatiquemetn par WX.
Le problème ici est qu'il peut y avoir un nombre indéfini de surcharge
de la classe et chaque surcharge intermédiaire doit être appelée.
Bien entendu, toutes ces méthodes sont virtuelles, et quand la super
classe envoit le message this->X(), c'est la méthode de la sous-classe
dont this est exactement l'instance qui est appelée. Tout ce qu'on lui
demande c'est d'assurer la post-condition. Le mieux pour elle c'est
d'appeler la méthode X de la superclasse mais si elle ne le fait pas
ce n'est pas grave, et de toutes façons WX s'assure que Y soit
appelée.
Michael DOUBEZ writes:Pascal J. Bourguignon wrote:Alexis Guillaume writes:Il y a le pattern "chaine de responsabilité" que tu peux employer. Ca a
l'air d'en être une forme (je ne peux pas dire avec les éléments que tu
donnes).
En effet je me renseigne sur ce pattern et ça a l'air d'être ce que je
veux faire. Merci beaucoup ! :-)
Oui, enfin, c'est le fonctionnement normal en programmation objet
quand on surcharge une méthode, d'appeler la méthode de la superclass.
C'est tellement normal, que dans les langages de programmation objet
normaux, il y a en général un mot clé ou une syntaxe particulière,
quand ce n'est pas automatique.
Ce qui ne marche que dans le cas où l'héritage est simple. Comme C++
supporte l'héritage multiple, c'est assez difficile de fournir un tel
mot clé.
Tu n'as pas bien fait attention aux exemples Common Lisp.
(call-next-method) appelle la méthode suivante, en tenant compte de
l'héritage multiple. Ce n'est pas forcément celle d'une super-classe.
Mais je n'entrerai pas plus dans la richesse du système objet de
Common Lisp.
Une autre solution serait d'utiliser une méthode crochet, c'est à dire
qu'on défini dans la super classe une méthode WX qui s'assure que la
méthode Y est appelée en même temps que X, la superclasse ne
définissant pas X (ou fournissant une définition par défaut vide), en
spécifiant simplement qu'une sous-classe doit surcharger X (comme elle
le veut, la méthode Y étant appelée automatiquemetn par WX.
Le problème ici est qu'il peut y avoir un nombre indéfini de surcharge
de la classe et chaque surcharge intermédiaire doit être appelée.
Bien entendu, toutes ces méthodes sont virtuelles, et quand la super
classe envoit le message this->X(), c'est la méthode de la sous-classe
dont this est exactement l'instance qui est appelée. Tout ce qu'on lui
demande c'est d'assurer la post-condition. Le mieux pour elle c'est
d'appeler la méthode X de la superclasse mais si elle ne le fait pas
ce n'est pas grave, et de toutes façons WX s'assure que Y soit
appelée.
On Mar 4, 4:27 pm, (Pascal J. Bourguignon)
wrote:Alexis Guillaume writes:
>> Il y a le pattern "chaine de responsabilité" que tu peux
>> employer. Ca a l'air d'en être une forme (je ne peux pas
>> dire avec les éléments que tu donnes).> En effet je me renseigne sur ce pattern et ça a l'air d'être
> ce que je veux faire. Merci beaucoup ! :-)Oui, enfin, c'est le fonctionnement normal en programmation
objet quand on surcharge une méthode, d'appeler la méthode de
la superclass.
Je dirais plutôt que c'est une symptome d'une mauvaise
conception. En général, on ne supplante que des fonctions
virtuelles pures.
C'est tellement normal, que dans les langages de programmation
objet normaux, il y a en général un mot clé ou une syntaxe
particulière, quand ce n'est pas automatique.
[...]
En somme, je crois qu'on est d'accord. Sauf que plutôt que de
compter sur la classe dérivée pour assurer le contrat de la
classe de base, il vaut mieux utiliser l'idiome standard, avec
une fonction virtuelle privée.
On Mar 4, 4:27 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
Alexis Guillaume <a...@free.fr> writes:
>> Il y a le pattern "chaine de responsabilité" que tu peux
>> employer. Ca a l'air d'en être une forme (je ne peux pas
>> dire avec les éléments que tu donnes).
> En effet je me renseigne sur ce pattern et ça a l'air d'être
> ce que je veux faire. Merci beaucoup ! :-)
Oui, enfin, c'est le fonctionnement normal en programmation
objet quand on surcharge une méthode, d'appeler la méthode de
la superclass.
Je dirais plutôt que c'est une symptome d'une mauvaise
conception. En général, on ne supplante que des fonctions
virtuelles pures.
C'est tellement normal, que dans les langages de programmation
objet normaux, il y a en général un mot clé ou une syntaxe
particulière, quand ce n'est pas automatique.
[...]
En somme, je crois qu'on est d'accord. Sauf que plutôt que de
compter sur la classe dérivée pour assurer le contrat de la
classe de base, il vaut mieux utiliser l'idiome standard, avec
une fonction virtuelle privée.
On Mar 4, 4:27 pm, (Pascal J. Bourguignon)
wrote:Alexis Guillaume writes:
>> Il y a le pattern "chaine de responsabilité" que tu peux
>> employer. Ca a l'air d'en être une forme (je ne peux pas
>> dire avec les éléments que tu donnes).> En effet je me renseigne sur ce pattern et ça a l'air d'être
> ce que je veux faire. Merci beaucoup ! :-)Oui, enfin, c'est le fonctionnement normal en programmation
objet quand on surcharge une méthode, d'appeler la méthode de
la superclass.
Je dirais plutôt que c'est une symptome d'une mauvaise
conception. En général, on ne supplante que des fonctions
virtuelles pures.
C'est tellement normal, que dans les langages de programmation
objet normaux, il y a en général un mot clé ou une syntaxe
particulière, quand ce n'est pas automatique.
[...]
En somme, je crois qu'on est d'accord. Sauf que plutôt que de
compter sur la classe dérivée pour assurer le contrat de la
classe de base, il vaut mieux utiliser l'idiome standard, avec
une fonction virtuelle privée.
On Mar 4, 5:20 pm, (Pascal J. Bourguignon)
wrote:Jean-Marc Bourguet writes:
> (Pascal J. Bourguignon) writes:>> Alexis Guillaume writes:>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).>> > En effet je me renseigne sur ce pattern et ça a l'air
>> > d'être ce que je veux faire. Merci beaucoup ! :-)>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode
>> de la superclass.> Bizarre, dans la plupart des cas, ce n'est pas ce que je
> fais (a commencer par tous les cas ou la fonction membre est
> pure dans la classe de base).Oui, les méthodes virtuelles pures sont pratiques pour
permettre au compilateur de vérifier qu'on a bien fourni une
implémentation dans les sous-classes. Cependant ce n'est pas
adapté à un style de programmation plus dynamique, et ça donne
plus de travail à l'utilisateur de la classe abstraite.En général je préfère définir une implémentation par défaut
pour toutes mes méthodes, même dans les classes "abstraites".
Ça me semble une violation du principe de la séparation des
concernes. La classe de base définit l'interface (le contrat),
non l'implémentation. Et dans la pratique, je ne vois pas
comment elle pourrait offir une implémentation par défaut. Une
implémentation consiste en un ensemble de fonctions membres qui
fonctionne d'une façon cohérente ensemble. Alors, on les
remplace toutes, ou on n'en remplace aucune.
On Mar 4, 5:20 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
Jean-Marc Bourguet <j...@bourguet.org> writes:
> p...@informatimago.com (Pascal J. Bourguignon) writes:
>> Alexis Guillaume <a...@free.fr> writes:
>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).
>> > En effet je me renseigne sur ce pattern et ça a l'air
>> > d'être ce que je veux faire. Merci beaucoup ! :-)
>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode
>> de la superclass.
> Bizarre, dans la plupart des cas, ce n'est pas ce que je
> fais (a commencer par tous les cas ou la fonction membre est
> pure dans la classe de base).
Oui, les méthodes virtuelles pures sont pratiques pour
permettre au compilateur de vérifier qu'on a bien fourni une
implémentation dans les sous-classes. Cependant ce n'est pas
adapté à un style de programmation plus dynamique, et ça donne
plus de travail à l'utilisateur de la classe abstraite.
En général je préfère définir une implémentation par défaut
pour toutes mes méthodes, même dans les classes "abstraites".
Ça me semble une violation du principe de la séparation des
concernes. La classe de base définit l'interface (le contrat),
non l'implémentation. Et dans la pratique, je ne vois pas
comment elle pourrait offir une implémentation par défaut. Une
implémentation consiste en un ensemble de fonctions membres qui
fonctionne d'une façon cohérente ensemble. Alors, on les
remplace toutes, ou on n'en remplace aucune.
On Mar 4, 5:20 pm, (Pascal J. Bourguignon)
wrote:Jean-Marc Bourguet writes:
> (Pascal J. Bourguignon) writes:>> Alexis Guillaume writes:>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).>> > En effet je me renseigne sur ce pattern et ça a l'air
>> > d'être ce que je veux faire. Merci beaucoup ! :-)>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode
>> de la superclass.> Bizarre, dans la plupart des cas, ce n'est pas ce que je
> fais (a commencer par tous les cas ou la fonction membre est
> pure dans la classe de base).Oui, les méthodes virtuelles pures sont pratiques pour
permettre au compilateur de vérifier qu'on a bien fourni une
implémentation dans les sous-classes. Cependant ce n'est pas
adapté à un style de programmation plus dynamique, et ça donne
plus de travail à l'utilisateur de la classe abstraite.En général je préfère définir une implémentation par défaut
pour toutes mes méthodes, même dans les classes "abstraites".
Ça me semble une violation du principe de la séparation des
concernes. La classe de base définit l'interface (le contrat),
non l'implémentation. Et dans la pratique, je ne vois pas
comment elle pourrait offir une implémentation par défaut. Une
implémentation consiste en un ensemble de fonctions membres qui
fonctionne d'une façon cohérente ensemble. Alors, on les
remplace toutes, ou on n'en remplace aucune.
Pascal J. Bourguignon wrote:Michael DOUBEZ writes:Une autre solution serait d'utiliser une méthode crochet, c'est à dire
qu'on défini dans la super classe une méthode WX qui s'assure que la
méthode Y est appelée en même temps que X, la superclasse ne
définissant pas X (ou fournissant une définition par défaut vide), en
spécifiant simplement qu'une sous-classe doit surcharger X (comme elle
le veut, la méthode Y étant appelée automatiquemetn par WX.
Le problème ici est qu'il peut y avoir un nombre indéfini de surcharge
de la classe et chaque surcharge intermédiaire doit être appelée.
Bien entendu, toutes ces méthodes sont virtuelles, et quand la super
classe envoit le message this->X(), c'est la méthode de la sous-classe
dont this est exactement l'instance qui est appelée. Tout ce qu'on lui
demande c'est d'assurer la post-condition. Le mieux pour elle c'est
d'appeler la méthode X de la superclasse mais si elle ne le fait pas
ce n'est pas grave, et de toutes façons WX s'assure que Y soit
appelée.
Ce qui ne marche que pour un niveau d'hériatge à moins que je n'ai mal
compris. Ici, cette post condition doit être réitérée dans chaque sous
classe pour elle même et pour sa future classe dérivée. A moins de
définir n fonctions membres (1 de plus dans chaque sous classe) je
vois pas comment faire.
Ce qu'il y ici c'est qu'il est difficile d'exprimer dans le langage la
séquence (next-class-to-call) pour réaliser le chainage; d'ailleur on
préfèrera la composition pour ce genre de cas.
Pour le cas de l'OP, il est peut ête possible de faire quelque chose
avec des templates (le problème est le method-hiding)), j'y
réfléchirai.
Pascal J. Bourguignon wrote:
Michael DOUBEZ <michael.doubez@free.fr> writes:
Une autre solution serait d'utiliser une méthode crochet, c'est à dire
qu'on défini dans la super classe une méthode WX qui s'assure que la
méthode Y est appelée en même temps que X, la superclasse ne
définissant pas X (ou fournissant une définition par défaut vide), en
spécifiant simplement qu'une sous-classe doit surcharger X (comme elle
le veut, la méthode Y étant appelée automatiquemetn par WX.
Le problème ici est qu'il peut y avoir un nombre indéfini de surcharge
de la classe et chaque surcharge intermédiaire doit être appelée.
Bien entendu, toutes ces méthodes sont virtuelles, et quand la super
classe envoit le message this->X(), c'est la méthode de la sous-classe
dont this est exactement l'instance qui est appelée. Tout ce qu'on lui
demande c'est d'assurer la post-condition. Le mieux pour elle c'est
d'appeler la méthode X de la superclasse mais si elle ne le fait pas
ce n'est pas grave, et de toutes façons WX s'assure que Y soit
appelée.
Ce qui ne marche que pour un niveau d'hériatge à moins que je n'ai mal
compris. Ici, cette post condition doit être réitérée dans chaque sous
classe pour elle même et pour sa future classe dérivée. A moins de
définir n fonctions membres (1 de plus dans chaque sous classe) je
vois pas comment faire.
Ce qu'il y ici c'est qu'il est difficile d'exprimer dans le langage la
séquence (next-class-to-call) pour réaliser le chainage; d'ailleur on
préfèrera la composition pour ce genre de cas.
Pour le cas de l'OP, il est peut ête possible de faire quelque chose
avec des templates (le problème est le method-hiding)), j'y
réfléchirai.
Pascal J. Bourguignon wrote:Michael DOUBEZ writes:Une autre solution serait d'utiliser une méthode crochet, c'est à dire
qu'on défini dans la super classe une méthode WX qui s'assure que la
méthode Y est appelée en même temps que X, la superclasse ne
définissant pas X (ou fournissant une définition par défaut vide), en
spécifiant simplement qu'une sous-classe doit surcharger X (comme elle
le veut, la méthode Y étant appelée automatiquemetn par WX.
Le problème ici est qu'il peut y avoir un nombre indéfini de surcharge
de la classe et chaque surcharge intermédiaire doit être appelée.
Bien entendu, toutes ces méthodes sont virtuelles, et quand la super
classe envoit le message this->X(), c'est la méthode de la sous-classe
dont this est exactement l'instance qui est appelée. Tout ce qu'on lui
demande c'est d'assurer la post-condition. Le mieux pour elle c'est
d'appeler la méthode X de la superclasse mais si elle ne le fait pas
ce n'est pas grave, et de toutes façons WX s'assure que Y soit
appelée.
Ce qui ne marche que pour un niveau d'hériatge à moins que je n'ai mal
compris. Ici, cette post condition doit être réitérée dans chaque sous
classe pour elle même et pour sa future classe dérivée. A moins de
définir n fonctions membres (1 de plus dans chaque sous classe) je
vois pas comment faire.
Ce qu'il y ici c'est qu'il est difficile d'exprimer dans le langage la
séquence (next-class-to-call) pour réaliser le chainage; d'ailleur on
préfèrera la composition pour ce genre de cas.
Pour le cas de l'OP, il est peut ête possible de faire quelque chose
avec des templates (le problème est le method-hiding)), j'y
réfléchirai.
James Kanze writes:
> On Mar 4, 5:20 pm, (Pascal J. Bourguignon)
> wrote:
>> Jean-Marc Bourguet writes:
>> > (Pascal J. Bourguignon) writes:
> Ça me semble une violation du principe de la séparation des
> concernes. La classe de base définit l'interface (le
> contrat), non l'implémentation. Et dans la pratique, je ne
> vois pas comment elle pourrait offir une implémentation par
> défaut. Une implémentation consiste en un ensemble de
> fonctions membres qui fonctionne d'une façon cohérente
> ensemble. Alors, on les remplace toutes, ou on n'en remplace
> aucune.
Alors toi, quand tu créé une nouvelle espèce de chien, tu
remplace toute la biologie et la réimplémente autrement? Il
n'y pas assez d'éléments pour autant d'espèces!
Bien au contraire, les promesses de la POO, à savoir
réutilisation, factorisation, productivité, etc vienne du fait
que l'on puisse implémenter une nouvelle fonctionnalité en
créant une sous-classe et en implémentant que les méthodes qui
diffèrent de la super-classe, en général il suffit de
surcharger un trés petit nombre de méthodes.
James Kanze <james.ka...@gmail.com> writes:
> On Mar 4, 5:20 pm, p...@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> Jean-Marc Bourguet <j...@bourguet.org> writes:
>> > p...@informatimago.com (Pascal J. Bourguignon) writes:
> Ça me semble une violation du principe de la séparation des
> concernes. La classe de base définit l'interface (le
> contrat), non l'implémentation. Et dans la pratique, je ne
> vois pas comment elle pourrait offir une implémentation par
> défaut. Une implémentation consiste en un ensemble de
> fonctions membres qui fonctionne d'une façon cohérente
> ensemble. Alors, on les remplace toutes, ou on n'en remplace
> aucune.
Alors toi, quand tu créé une nouvelle espèce de chien, tu
remplace toute la biologie et la réimplémente autrement? Il
n'y pas assez d'éléments pour autant d'espèces!
Bien au contraire, les promesses de la POO, à savoir
réutilisation, factorisation, productivité, etc vienne du fait
que l'on puisse implémenter une nouvelle fonctionnalité en
créant une sous-classe et en implémentant que les méthodes qui
diffèrent de la super-classe, en général il suffit de
surcharger un trés petit nombre de méthodes.
James Kanze writes:
> On Mar 4, 5:20 pm, (Pascal J. Bourguignon)
> wrote:
>> Jean-Marc Bourguet writes:
>> > (Pascal J. Bourguignon) writes:
> Ça me semble une violation du principe de la séparation des
> concernes. La classe de base définit l'interface (le
> contrat), non l'implémentation. Et dans la pratique, je ne
> vois pas comment elle pourrait offir une implémentation par
> défaut. Une implémentation consiste en un ensemble de
> fonctions membres qui fonctionne d'une façon cohérente
> ensemble. Alors, on les remplace toutes, ou on n'en remplace
> aucune.
Alors toi, quand tu créé une nouvelle espèce de chien, tu
remplace toute la biologie et la réimplémente autrement? Il
n'y pas assez d'éléments pour autant d'espèces!
Bien au contraire, les promesses de la POO, à savoir
réutilisation, factorisation, productivité, etc vienne du fait
que l'on puisse implémenter une nouvelle fonctionnalité en
créant une sous-classe et en implémentant que les méthodes qui
diffèrent de la super-classe, en général il suffit de
surcharger un trés petit nombre de méthodes.
James Kanze writes:
> On Mar 4, 4:27 pm, (Pascal J. Bourguignon)
> wrote:
>> Alexis Guillaume writes:
>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).
>> > En effet je me renseigne sur ce pattern et ça a l'air d'être
>> > ce que je veux faire. Merci beaucoup ! :-)
>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode de
>> la superclass.
> Je dirais plutôt que c'est une symptome d'une mauvaise
> conception. En général, on ne supplante que des fonctions
> virtuelles pures.
Pas en POO. L'héritage est un élément essentiel de la POO.
OO = {héritage, encapsulation, polymorphisme, abstraction}.
Si on se contente de définir des interfaces d'un côté, et de
les implémenter d'un autre, on exclu l'héritage, et on ne
programme pas en OO.
>> C'est tellement normal, que dans les langages de
>> programmation objet normaux, il y a en général un mot clé
>> ou une syntaxe particulière, quand ce n'est pas
>> automatique.
> [...]
> En somme, je crois qu'on est d'accord. Sauf que plutôt que
> de compter sur la classe dérivée pour assurer le contrat de
> la classe de base, il vaut mieux utiliser l'idiome standard,
> avec une fonction virtuelle privée.
On est d'accord, cependant le principe de substitution de
Lyskov va plus loin: une sous-classe peut accepter une version
plus large de la pré-condition, et assurer une version plus
stricte de la post-condition.
Ainsi, l'idiome que tu appelles "consacré" n'est pas du tout
adapté.
class Automobile {
public:
virtual void attachWheel(Wheel* w);
// PRE: w->isCircular() and (0<=this->wheelCount()<this->maxWheelC ount())
// POST: this->hasWheel(w) and (this->wheelCount() == 1 + old thi s->wheelCount())
// and (for all v in this->wheels(), (v == w) or (old this- >hasWheel(v)))
// ...
};
class RusticAutomobile : public Automobile {
virtual void attachWheel(Wheel* w);
// PRE: (0<this->wheelCount()<=this->maxWheelCount())
// POST: this->hasWheel(w) and (0<this->wheelCount()<=this->maxWhee lCount())
// and (for all v in this->wheels(), (v == w) or (old this- >hasWheel(v)))
// ...
};
void RusticAutomobile::attachWheel(Wheel* w)
{
assert(0<this->wheelCount()<=this->maxWheelCount()); // RusticAutom obile precond
if(not w->isCircular()){
Hammer* h=new Hammer(Hammer::BIG);
while(not w->isCircular()){
h->bangOn(w);
}
}
assert(w->isCircular() and (0<this->wheelCount()<=this->maxWheelCou nt()));
if(this->wheelCount()==this->maxWheelCount()){
this->removeWheel(this->selectOldestWhell());
}
assert(w->isCircular()
and (0<this->wheelCount()<this->maxWheelCount())); // Automobi le precond
Automobile::attachWheel(w);
assert(this->hasWheel(w) and (this->wheelCount() == 1 + old this- >wheelCount())
and (for all v in this->wheels(), (v == w) or (old this->h asWheel(v))));
// Automobile postcond
// ==>
assert(this->hasWheel(w) and (0<this->wheelCount()<=this->maxWheelC ount())
and (for all v in this->wheels(), (v == w) or (old this->h asWheel(v))));
// RusticAutomobile postcond
}
Mais ce qui est intéressant maintenant, c'est que comme
preCondition(Automobile::attachWheel) ==> preCondition(RusticAutomobi le::attachWheel)
et
postCondition(RusticAutomobile::attachWheel) ==> postCondition(Automo bile::attachWheel)
on peut substituer une automobile rustique partout où on a une
automobile, et ça continuera à fonctionner.
Automobile* a = new RusticAutomobile();
road->materialize(a); // road n'a pas besoin de savoir qu'il existe des
// automobiles rustiques, pour lui, toutes les
// automobiles sont des Automobiles.
Et on pourra, dans le code qui sait que l'automobile est
rustique, y attacher des roues carrées.
RusticAutomobile* r = new RusticAutomobile();
r->attachWheel(new Wheel(Wheel::SQUARE));
road->materialize(r);
James Kanze <james.ka...@gmail.com> writes:
> On Mar 4, 4:27 pm, p...@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> Alexis Guillaume <a...@free.fr> writes:
>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).
>> > En effet je me renseigne sur ce pattern et ça a l'air d'être
>> > ce que je veux faire. Merci beaucoup ! :-)
>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode de
>> la superclass.
> Je dirais plutôt que c'est une symptome d'une mauvaise
> conception. En général, on ne supplante que des fonctions
> virtuelles pures.
Pas en POO. L'héritage est un élément essentiel de la POO.
OO = {héritage, encapsulation, polymorphisme, abstraction}.
Si on se contente de définir des interfaces d'un côté, et de
les implémenter d'un autre, on exclu l'héritage, et on ne
programme pas en OO.
>> C'est tellement normal, que dans les langages de
>> programmation objet normaux, il y a en général un mot clé
>> ou une syntaxe particulière, quand ce n'est pas
>> automatique.
> [...]
> En somme, je crois qu'on est d'accord. Sauf que plutôt que
> de compter sur la classe dérivée pour assurer le contrat de
> la classe de base, il vaut mieux utiliser l'idiome standard,
> avec une fonction virtuelle privée.
On est d'accord, cependant le principe de substitution de
Lyskov va plus loin: une sous-classe peut accepter une version
plus large de la pré-condition, et assurer une version plus
stricte de la post-condition.
Ainsi, l'idiome que tu appelles "consacré" n'est pas du tout
adapté.
class Automobile {
public:
virtual void attachWheel(Wheel* w);
// PRE: w->isCircular() and (0<=this->wheelCount()<this->maxWheelC ount())
// POST: this->hasWheel(w) and (this->wheelCount() == 1 + old thi s->wheelCount())
// and (for all v in this->wheels(), (v == w) or (old this- >hasWheel(v)))
// ...
};
class RusticAutomobile : public Automobile {
virtual void attachWheel(Wheel* w);
// PRE: (0<this->wheelCount()<=this->maxWheelCount())
// POST: this->hasWheel(w) and (0<this->wheelCount()<=this->maxWhee lCount())
// and (for all v in this->wheels(), (v == w) or (old this- >hasWheel(v)))
// ...
};
void RusticAutomobile::attachWheel(Wheel* w)
{
assert(0<this->wheelCount()<=this->maxWheelCount()); // RusticAutom obile precond
if(not w->isCircular()){
Hammer* h=new Hammer(Hammer::BIG);
while(not w->isCircular()){
h->bangOn(w);
}
}
assert(w->isCircular() and (0<this->wheelCount()<=this->maxWheelCou nt()));
if(this->wheelCount()==this->maxWheelCount()){
this->removeWheel(this->selectOldestWhell());
}
assert(w->isCircular()
and (0<this->wheelCount()<this->maxWheelCount())); // Automobi le precond
Automobile::attachWheel(w);
assert(this->hasWheel(w) and (this->wheelCount() == 1 + old this- >wheelCount())
and (for all v in this->wheels(), (v == w) or (old this->h asWheel(v))));
// Automobile postcond
// ==>
assert(this->hasWheel(w) and (0<this->wheelCount()<=this->maxWheelC ount())
and (for all v in this->wheels(), (v == w) or (old this->h asWheel(v))));
// RusticAutomobile postcond
}
Mais ce qui est intéressant maintenant, c'est que comme
preCondition(Automobile::attachWheel) ==> preCondition(RusticAutomobi le::attachWheel)
et
postCondition(RusticAutomobile::attachWheel) ==> postCondition(Automo bile::attachWheel)
on peut substituer une automobile rustique partout où on a une
automobile, et ça continuera à fonctionner.
Automobile* a = new RusticAutomobile();
road->materialize(a); // road n'a pas besoin de savoir qu'il existe des
// automobiles rustiques, pour lui, toutes les
// automobiles sont des Automobiles.
Et on pourra, dans le code qui sait que l'automobile est
rustique, y attacher des roues carrées.
RusticAutomobile* r = new RusticAutomobile();
r->attachWheel(new Wheel(Wheel::SQUARE));
road->materialize(r);
James Kanze writes:
> On Mar 4, 4:27 pm, (Pascal J. Bourguignon)
> wrote:
>> Alexis Guillaume writes:
>> >> Il y a le pattern "chaine de responsabilité" que tu peux
>> >> employer. Ca a l'air d'en être une forme (je ne peux pas
>> >> dire avec les éléments que tu donnes).
>> > En effet je me renseigne sur ce pattern et ça a l'air d'être
>> > ce que je veux faire. Merci beaucoup ! :-)
>> Oui, enfin, c'est le fonctionnement normal en programmation
>> objet quand on surcharge une méthode, d'appeler la méthode de
>> la superclass.
> Je dirais plutôt que c'est une symptome d'une mauvaise
> conception. En général, on ne supplante que des fonctions
> virtuelles pures.
Pas en POO. L'héritage est un élément essentiel de la POO.
OO = {héritage, encapsulation, polymorphisme, abstraction}.
Si on se contente de définir des interfaces d'un côté, et de
les implémenter d'un autre, on exclu l'héritage, et on ne
programme pas en OO.
>> C'est tellement normal, que dans les langages de
>> programmation objet normaux, il y a en général un mot clé
>> ou une syntaxe particulière, quand ce n'est pas
>> automatique.
> [...]
> En somme, je crois qu'on est d'accord. Sauf que plutôt que
> de compter sur la classe dérivée pour assurer le contrat de
> la classe de base, il vaut mieux utiliser l'idiome standard,
> avec une fonction virtuelle privée.
On est d'accord, cependant le principe de substitution de
Lyskov va plus loin: une sous-classe peut accepter une version
plus large de la pré-condition, et assurer une version plus
stricte de la post-condition.
Ainsi, l'idiome que tu appelles "consacré" n'est pas du tout
adapté.
class Automobile {
public:
virtual void attachWheel(Wheel* w);
// PRE: w->isCircular() and (0<=this->wheelCount()<this->maxWheelC ount())
// POST: this->hasWheel(w) and (this->wheelCount() == 1 + old thi s->wheelCount())
// and (for all v in this->wheels(), (v == w) or (old this- >hasWheel(v)))
// ...
};
class RusticAutomobile : public Automobile {
virtual void attachWheel(Wheel* w);
// PRE: (0<this->wheelCount()<=this->maxWheelCount())
// POST: this->hasWheel(w) and (0<this->wheelCount()<=this->maxWhee lCount())
// and (for all v in this->wheels(), (v == w) or (old this- >hasWheel(v)))
// ...
};
void RusticAutomobile::attachWheel(Wheel* w)
{
assert(0<this->wheelCount()<=this->maxWheelCount()); // RusticAutom obile precond
if(not w->isCircular()){
Hammer* h=new Hammer(Hammer::BIG);
while(not w->isCircular()){
h->bangOn(w);
}
}
assert(w->isCircular() and (0<this->wheelCount()<=this->maxWheelCou nt()));
if(this->wheelCount()==this->maxWheelCount()){
this->removeWheel(this->selectOldestWhell());
}
assert(w->isCircular()
and (0<this->wheelCount()<this->maxWheelCount())); // Automobi le precond
Automobile::attachWheel(w);
assert(this->hasWheel(w) and (this->wheelCount() == 1 + old this- >wheelCount())
and (for all v in this->wheels(), (v == w) or (old this->h asWheel(v))));
// Automobile postcond
// ==>
assert(this->hasWheel(w) and (0<this->wheelCount()<=this->maxWheelC ount())
and (for all v in this->wheels(), (v == w) or (old this->h asWheel(v))));
// RusticAutomobile postcond
}
Mais ce qui est intéressant maintenant, c'est que comme
preCondition(Automobile::attachWheel) ==> preCondition(RusticAutomobi le::attachWheel)
et
postCondition(RusticAutomobile::attachWheel) ==> postCondition(Automo bile::attachWheel)
on peut substituer une automobile rustique partout où on a une
automobile, et ça continuera à fonctionner.
Automobile* a = new RusticAutomobile();
road->materialize(a); // road n'a pas besoin de savoir qu'il existe des
// automobiles rustiques, pour lui, toutes les
// automobiles sont des Automobiles.
Et on pourra, dans le code qui sait que l'automobile est
rustique, y attacher des roues carrées.
RusticAutomobile* r = new RusticAutomobile();
r->attachWheel(new Wheel(Wheel::SQUARE));
road->materialize(r);