Ce cours concerne le polymorphisme, c'est-à-dire la capacité pour une variable de prendre plusieurs types de donnée durant le temps de sa vie. Le cours présente aussi la notion de classe abstraite et d'interface qui est une classe abstraite pure.
Python avancé : Interface graphique et programmation évènementielle
Polymorphisme, interface et classe abstraite
1. PO3T Programmation orientée objet
Séance 4
Polymorphisme, interface et
classe abstraite
Sébastien Combéfis, Quentin Lurkin lundi 12 octobre 2015
2. Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons
Attribution – Pas d’Utilisation Commerciale – Pas de Modification 4.0 International.
3. Rappels
Extension d’une classe existante par héritage
Une sous-classe étend une super-classe
Relation is-a
Différence composition/héritage
Redéfinition de méthode
Modification d’une méthode existante de la super-classe
Accès aux membres de la super-classe depuis la sous-classe
Visibilité des membres (public/private/protected)
3
6. Type d’un objet
Un objet peut avoir plusieurs types
Une instance d’une sous-classe est aussi une de la super-classe
Méthodes accessibles sont celles de toute la hiérarchie
Car les méthodes héritées sont les méthodes accessibles
Une référence polymorphique peut être convertie
Conversion vers n’importe quel type compatible
6
7. Représenter un point du plan
Point dans le plan en coordonnées entières
Méthode translate qui renvoie un nouveau point translaté
1 public class Point
2 {
3 private readonly int x, y;
4
5 public int X { get { return x; } }
6 public int Y { get { return y; } }
7
8 public Point (int x, int y)
9 {
10 this.x = x;
11 this.y = y;
12 }
13
14 public Point translate (int dx , int dy)
15 {
16 return new Point (x + dx , y + dy);
17 }
18 }
7
8. Point coloré
Extension de la classe Point
Ajout d’une variable d’instance pour stocker la couleur
Appel du constructeur de la super-classe pour stocker x et y
Et code spécifique pour stocker colour
1 public class ColouredPoint : Point
2 {
3 private readonly Color colour;
4
5 public Color Colour { get { return colour; } }
6
7 public ColouredPoint (int x, int y, Color colour) : base (x, y)
8 {
9 this.colour = colour;
10 }
11 }
8
10. Redéfinition de méthode
Redéfinition d’une méthode
Remplacement de la version de la super-classe
Représentation en chaine de caractères d’un point coloré
Se base sur la méthode déjà présente dans la super-classe
1 public class ColouredPoint
2 {
3 // ...
4
5 public override string ToString ()
6 {
7 return string.Format("{0} [colour ={1}]", base.ToString (),
colour);
8 }
9 }
10
11. Référence polymorphique (1)
Redéfinition d’une méthode
Remplacement de la version de la super-classe
Représentation en chaine de caractères d’un point coloré
Se base sur la méthode déjà présente dans la super-classe
1 public class Program
2 {
3 public static void Main ( string [] args)
4 {
5 Point p = new Point (12, -5);
6 Console .WriteLine (p);
7
8 ColouredPoint q = new ColouredPoint (7, 5, Color.FromArgb
(255 , 0, 0));
9 Console .WriteLine (q);
10 }
11 }
11
13. Référence polymorphique (3)
Tester le type dynamique d’une référence
Test de la relation is-a
Opérateur de test de la compatibilité de type
Variable contient une référence vers un objet d’un type donné
1 Console .WriteLine ((p is Point) + " / " + (p is ColouredPoint ));
2 Console .WriteLine ((q is Point) + " / " + (q is ColouredPoint ));
True / False
True / True
13
14. Polymorphisme (1)
Différence type d’une variable et celui de l’objet référencé
Type statique et type dynamique
Une variable peut avoir plusieurs types par polymorphisme
Ces différents types doivent être compatibles
1 ColouredPoint p1;
2 Point p2;
3
4 // Type statique ColouredPoint et type dynamique ColouredPoint
5 p1 = new ColouredPoint (-1, 2, Color.FromArgb (0, 255, 0));
6
7 // Type statique Point et type dynamique ColouredPoint
8 p2 = new ColouredPoint (8, 1, Color.FromArgb (0, 0, 255));
14
15. Polymorphisme (2)
Type statique
Type utilisé lors de la déclaration de la variable
Déterminé une fois pour toute à la déclaration
Limite les références qui pourront être stockées
Uniquement pour les langages typés statiquement
Type dynamique
Type de l’objet référencé par la variable
Déterminé lors de l’exécution
Peut changer en cours d’exécution
15
16. Conversion implicite
Objet d’une sous-classe dans variable d’une super-classe
Conversion implicite de la référence
1 ColouredPoint p;
2 Point q;
3
4 p = new ColouredPoint (7, 5, Color.FromArgb (255 , 0, 0));
5 q = p;
6 Console .WriteLine (q);
(7, 5) [colour=Color [A=255 , R=255 , G=0, B=0]]
16
17. Conversion explicite
Variable d’une super-classe vers objet d’une sous-classe
Conversion explicite de la référence
1 Point p;
2 ColouredPoint q;
3
4 p = new ColouredPoint (7, 5, Color.FromArgb (255 , 0, 0));
5 if (p is ColouredPoint )
6 {
7 q = ( ColouredPoint ) p;
8 Console .WriteLine (q);
9 }
(7, 5) [colour=Color [A=255 , R=255 , G=0, B=0]]
17
18. Type compatible
Deux types compatibles sont liés par une relation de filiation
Sous-classe compatible avec super-classe
Attention, la compatibilité est unidirectionnelle
Dans le sens de la relation is-a
Conversions si type dynamique de b compatible avec A
Implicite : A a = b;
Explicite : A a = (A) b;
18
19. Résolution des appels de méthode (1)
Le type statique détermine les méthodes accessibles
Les seules méthodes accessibles sont celles du type statique
La méthode effectivement appelée dépend du type dynamique
En cas de redéfinition, la méthode de la sous-classe est appelée
Résolution en deux étapes
Méthodes accessibles vérifiées à la compilation
Méthode appelée décidée à l’exécution
19
20. Résolution des appels de méthode (2)
Méthode sous-classe non accessible
Car le type statique est celui de la super-classe
1 Point p = new ColouredPoint (7, 5, Color.FromArgb (255 , 0, 0));
2
3 Console .WriteLine (p.Colour);
error CS1061: Type ‘Cours4.Point ’ does not contain a definition
for ‘Colour ’ and no extension method ‘Colour ’ of type ‘Cours4.
Point ’ could be found. Are you missing an assembly reference?
20
21. Résolution des appels de méthode (3)
Méthode redéfinie dans sous-classe appelée
Même si type statique de la variable est de la super-classe
1 Point p = new ColouredPoint (7, 5, Color.FromArgb (255 , 0, 0));
2
3 Console .WriteLine (p.ToString ());
(7, 5) [colour=Color [A=255 , R=255 , G=0, B=0]]
21
22. Résolution des appels de méthode (4)
Méthode héritée accessible dans la sous-classe
La méthode translate est héritée par la sous-classe
Le type de retour de la méthode reste néanmoins Point
Comment faire pour renvoyer un ColouredPoint ?
1 ColouredPoint p = new ColouredPoint (7, 5, Color.FromArgb (255 , 0,
0));
2
3 Console .WriteLine (p.Translate (1, -1));
(8, 4)
22
25. Et en Java... (1)
1 public class Point
2 {
3 private final int x, y;
4
5 public Point (int x, int y)
6 {
7 this.x = x;
8 this.y = y;
9 }
10
11 public int getX () { return x; }
12 public int getY () { return y; }
13
14 public Point translate (int dx , int dy)
15 {
16 return new Point (x + dx , y + dy);
17 }
18
19 @Override
20 public String toString ()
21 {
22 return String.format ("(%d, %d)", x, y);
23 }
24 }
25
26. Et en Java... (2)
1 class ColouredPoint extends Point
2 {
3 private final Color colour;
4
5 public ColouredPoint (int x, int y, Color colour)
6 {
7 super (x, y);
8 this.colour = colour;
9 }
10
11 @Override
12 public String toString ()
13 {
14 return String.format ("%s [colour =%s]", super.toString (),
colour);
15 }
16 }
1 ColouredPoint q = new ColouredPoint (7, 5, Color.RED);
2 System.out.println (q);
3
4 System.out.println (q instanceof ColouredPoint );
26
27. Et en PHP... (1)
1 <?php
2 class Point
3 {
4 private $x , $y;
5
6 public function __construct ($x , $y)
7 {
8 $this ->x = $x;
9 $this ->y = $y;
10 }
11
12 public function translate ($dx , $dy)
13 {
14 return new Point ($this ->x + $dx , $this ->y + $dy);
15 }
16
17 public function __toString ()
18 {
19 return sprintf ("(%d, %d)", $this ->x, $this ->y);
20 }
21 }
22
23 $p = new Point (12, -5);
24 echo $p;
25 ?>
27
28. Et en PHP... (2)
1 <?php
2 class ColouredPoint extends Point
3 {
4 private $colour;
5
6 public function __construct ($x , $y , $colour)
7 {
8 parent :: __construct ($x , $y);
9 $this ->colour = $colour;
10 }
11
12 public function __toString ()
13 {
14 return sprintf ("%s (%s)", parent :: __toString (), $this ->
colour);
15 }
16 }
17
18 $q = new ColouredPoint (7, 5, "red");
19 echo $q;
20 ?>
28
30. Interface
Une interface ne contient que des entêtes de méthode
Permet de définir un contrat avec un utilisateur
1 public interface Complex
2 {
3 // Partie réelle du complexe
4 public double real ();
5
6 // Partie imaginaire du complexe
7 public double imag ();
8
9 // Module du complexe
10 public double abs ();
11
12 // Argument du complexe
13 public double arg ();
14 }
30
31. Relation d’implémentation
Une classe peut implémenter une interface
Doit fournir un corps pour toutes les méthodes de l’interface
1 public class CartesianComplex implements Complex
2 {
3 private final double a, b;
4
5 // a + bi
6 public CartesianComplex (double a, double b)
7 {
8 this.a = a;
9 this.b = b;
10 }
11
12 public double real () { return a; }
13 public double imag () { return b; }
14 public double abs () { return Math.sqrt (a * a + b * b); }
15 public double arg () { return Math.acos (a / abs ()); }
16 }
31
32. Polymorphisme (1)
Plusieurs classes peuvent implémenter la même interface
Permet du polymorphisme comme l’héritage
1 class PolarComplex implements Complex
2 {
3 private final double r, theta;
4
5 public PolarComplex (double r, double theta)
6 {
7 this.r = r;
8 this.theta = theta;
9 }
10
11 public double real () { return r * Math.cos (theta); }
12 public double imag () { return r * Math.sin (theta); }
13 public double abs () { return r; }
14 public double arg () { return theta; }
15 }
32
33. Polymorphisme (2)
Somme de nombres complexes facilitée par polymorphisme
Il suffit de passer par les méthodes de l’interface
1 List <Complex > list = new ArrayList <Complex >();
2 list.add (new CartesianComplex (2, -1));
3 list.add (new PolarComplex (2, Math.PI / 2));
4 list.add (new CartesianComplex (-1, 1));
5
6 double a = 0;
7 double b = 0;
8 for (Complex c : list)
9 {
10 a += c.real ();
11 b += c.imag ();
12 }
13 Complex sum = new CartesianComplex (a, b);
14 System.out.println (sum);
1 ,000000 + 2 ,000000 i
33
34. Implémentation multiple
Une classe peut implémenter plusieurs interfaces
Ce qui lève la limitation des langages sans héritage multiple
La relation implements est également une relation is-a
Shape Drawable
Square
34
35. Classe abstraite (1)
Intermédiaire entre l’interface et la classe concrète
Certaines méthodes communes peuvent être implémentées
1 public abstract class Complex
2 {
3 public abstract double real ();
4 public abstract double imag ();
5
6 public double abs ()
7 {
8 double a = real ();
9 double b = imag ();
10 return Math.sqrt (a * a + b * b);
11 }
12
13 public double arg ()
14 {
15 return Math.acos (real () / abs ());
16 }
17 }
35
36. Classe abstraite (2)
Une classe abstraite est étendue en classe concrète
Il faut définir le corps des méthodes abstraites
1 class PolarComplex extends Complex
2 {
3 private final double r, theta;
4
5 public PolarComplex (double r, double theta)
6 {
7 this.r = r;
8 this.theta = theta;
9 }
10
11 public double real () { return r * Math.cos (theta); }
12 public double imag () { return r * Math.sin (theta); }
13
14 @Override public double abs () { return r; }
15 @Override public double arg () { return theta; }
16 }
36
37. Hiérarchie de classe
Une interface est une classe abstraite pure
Et ne contenant pas de constructeur, ni de variables d’instance
Une sous-classe d’une classe abstraite peut être abstraite
Si elle ne fournit pas de corps à toutes les méthodes abstraites
Permet d’éviter de la duplication de code
En rassemblant les membres communs dans la super-classe
37
39. Porte logique (1)
Implémentation d’un circuit de portes logiques
Une porte logique générique possède n entrées et une sortie
Connectée à une entrée d’une autre porte logique en sortie
1 public abstract class Gate
2 {
3 private boolean [] in;
4 protected boolean out;
5 private String name;
6 private Gate outgate;
7 private int outnum;
8
9 public Gate ( String n, int nbIn)
10 {
11 name = n;
12 in = new boolean [nbIn ];
13 }
39
40. Porte logique (2)
1 public void setIn (int num , boolean state)
2 {
3 in[num] = state;
4 boolean oldout = out;
5
6 update ();
7
8 if (outgate != null && out != oldout)
9 {
10 outgate.setIn (outnum , out);
11 }
12 }
13
14 public void connect (Gate g, int in)
15 {
16 outgate = g;
17 outnum = in;
18 }
19
20 public int getInNb ()
21 {
22 return in.length;
23 }
40
41. Porte logique (3)
1 public boolean getIn (int num)
2 {
3 return in[num];
4 }
5
6 public String toString ()
7 {
8 StringBuffer buff = new StringBuffer ();
9 buff.append (name).append (" :ntIn :t");
10 for (int i = 0; i < in.length; i++)
11 {
12 buff.append (in[i] ? ’1’ : ’0’).append (’ ’);
13 }
14 buff.append ("ntOut :t").append (out ? ’1’ : ’0’);
15 return buff.append ("n").toString ();
16 }
17
18 // Méthode de mise à jour de l’état de la porte logique
19 // et de mise à jour de l’entrée du voisin
20 protected abstract void update ();
21 }
41
42. Porte AND et porte NOT
1 public class AndGate extends Gate
2 {
3 public AndGate (String name)
4 {
5 super (name , 2);
6 }
7
8 @Override
9 protected void update ()
10 {
11 out = getIn (0) && getIn (1);
12 }
13 }
14
15 public class NotGate extends Gate
16 {
17 public NotGate (String name)
18 {
19 super (name , 1);
20 }
21
22 @Override
23 protected void update ()
24 {
25 out = ! getIn (0);
26 }
27 }
42
43. Circuit logique
1 Gate g1 = new OrGate ("g1");
2 Gate g2 = new NotGate ("g2");
3
4 g1.connect (g2 , 0);
5
6 g1.setIn (1, true);
7 g2.setIn (0, true);
8
9 System.out.println (g1);
10 System.out.println (g2);
g1 :
In : 0 1
Out : 1
g2 :
In : 1
Out : 0
43