MODULE TransportsAbstrait; TYPE Véhicule = POINTER TO ABSTRACT RECORD prix: REAL; nbpassmax: INTEGER ChoseTransportée =POINTER TO ABSTRACT RECORD poids: REAL; ident: ARRAY 81 OF CHAR PROCEDURE (v: Véhicule) Coût (nbpass: INTEGER; dist: REAL): REAL, NEW, ABSTRACT; PROCEDURE (v: Véhicule) ToString (): POINTER TO ARRAY OF CHAR, NEW, ABSTRACT; PROCEDURE (c: ChoseTransportée) ToString (): POINTER TO ARRAY OF CHAR, NEW, ABSTRACT; END TransportsAbstrait. Rappel : Un TYPE est la définition d'une entité regroupant des caractéristiques communes. Une classe est un TYPE où les caractéristiques regroupées sont les données et les traitements Une classe abstraite c'est la définition d'une pure forme dont on ne pourra créer d'instances. On devra, pour l'employer, créer des classes dérivées concrètes. La classe abstraite est obtenue à l'issue de l'analyse par factorisation de caractéristiques communes. Remarques : Un module peut encapsuler plusieurs classes. Ici, il y en a 2. Deux classes différentes peuvent avoir des méthodes de même nom (ici ToString). Tout comme deux champs données de deux RECORDs différents peuvent avoir le même nom.
package transports; / Véhicule.java public abstract class Véhicule { public double prix; public int nbpassmax; public abstract double coût (int nbpass, double dist ); public abstract String tostring (); } // Véhicule package transports; / ChoseTransportée.java public abstract class ChoseTransportée { public double poids; public String ident; public abstract String tostring (); } // ChoseTransportée
MODULE TransportsConcret; TransportsAbstrait, ZStrPlus; TYPE Voiture = POINTER TO EXTENSIBLE RECORD (TransportsAbstrait.Véhicule) nbportes: INTEGER Moto = POINTER TO EXTENSIBLE RECORD (TransportsAbstrait.Véhicule) sélecteurgauche: BOOLEAN Passager = POINTER TO RECORD (TransportsAbstrait.ChoseTransportée) classe: INTEGER ( 1e, 2e ou 3e classe ) Fret = POINTER TO RECORD (TransportsAbstrait.ChoseTransportée) prix: REAL PROCEDURE (v: Voiture) Coût (nbpass: INTEGER; dist: REAL): REAL, EXTENSIBLE; BEGIN (Coût) RETURN 10distnbPass; ( Pourrait être affiné, ce n'est pas le but ici. ) END Coût; PROCEDURE (v: Voiture) ToString (): POINTER TO ARRAY OF CHAR, EXTENSIBLE ; BEGIN (ToString) RETURN ZStrPlus.DynStr("Voiture") END ToString; PROCEDURE (m: Moto) Coût (nbpass: INTEGER; dist: REAL): REAL, EXTENSIBLE; BEGIN (Coût) RETURN 4distnbPass; END Coût; PROCEDURE (m: Moto) ToString (): POINTER TO ARRAY OF CHAR, EXTENSIBLE; BEGIN (ToString) RETURN ZStrPlus.DynStr("Moto") END ToString; PROCEDURE (p: Passager) ToString (): POINTER TO ARRAY OF CHAR; BEGIN (ToString) RETURN ZStrPlus.DynStr(p.ident) ( L'identificateur p désigne l'instance courante. ) END ToString; PROCEDURE (f: Fret) ToString (): POINTER TO ARRAY OF CHAR; BEGIN (ToString) RETURN ZStrPlus.DynStr(f.ident) ( L'identificateur f désigne l'instance courante. ) END ToString; END TransportsConcret. On doit implanter explicitement toutes les méthodes ABSTRACT héritées de la classe mère.
package transports; / Voiture.java public class Voiture extends Véhicule { public int nbportes; public double coût (int nbpass, double dist ) { return 10 dist nbpass; // Pourrait être affiné, ce n'est pas le but ici } // coût public String tostring () { return "Voiture" ; } // tostring } // Voiture package transports; / Moto.java public class Moto extends Véhicule { public boolean sélecteurgauche; public double coût (int nbpass, double dist ) { return 4 dist nbpass; } // coût public String tostring () { return "Moto" ; } // tostring } // Moto package transports; / Passager.java public class Passager extends ChoseTransportée { public int classe; // 1e, 2e ou 3e classe public String tostring () { return this.ident; // Le this n'est ici pas nécessaire } // tostring } // Passager package transports; / Fret.java public class Fret extends ChoseTransportée { public double prix; public String tostring () { return this.ident; // Le this n'est ici pas nécessaire } // tostring } // Fret
MODULE TransportsConcretExt; TransportsConcret, ZStrPlus; TYPE QuatreQuatre = POINTER TO EXTENSIBLE RECORD (TransportsConcret.Voiture) MonoSpace = POINTER TO EXTENSIBLE RECORD (TransportsConcret.Voiture) SideCar = POINTER TO EXTENSIBLE RECORD (TransportsConcret.Moto) PROCEDURE (q: QuatreQuatre) AutreMéthode (), NEW ; BEGIN (AutreMéthode) END AutreMéthode; PROCEDURE (q: QuatreQuatre) Coût (nbpass: INTEGER; dist: REAL): REAL, EXTENSIBLE; BEGIN (Coût) RETURN 15distnbPass; END Coût; PROCEDURE (q: QuatreQuatre) ToString (): POINTER TO ARRAY OF CHAR, EXTENSIBLE; BEGIN (ToString) RETURN ZStrPlus.DynStr("4x4") END ToString; PROCEDURE (m: MonoSpace) Coût (nbpass: INTEGER; dist: REAL): REAL, EXTENSIBLE; BEGIN (Coût) RETURN 11distnbPass; END Coût; PROCEDURE (m: MonoSpace) ToString (): POINTER TO ARRAY OF CHAR, EXTENSIBLE; BEGIN (ToString) RETURN ZStrPlus.DynStr("Monospace") END ToString; PROCEDURE (s: SideCar) Coût (nbpass: INTEGER; dist: REAL): REAL, EXTENSIBLE; BEGIN (Coût) RETURN 4distnbPass; END Coût; PROCEDURE (s: SideCar) ToString (): POINTER TO ARRAY OF CHAR, EXTENSIBLE; BEGIN (ToString) RETURN ZStrPlus.DynStr("SideCar") END ToString; END TransportsConcretExt.
package transports; / QuatreQuatre.java public class QuatreQuatre extends Voiture { public final void autreméthode () { } // autreméthode public double coût (int nbpass, double dist ) { return 15 dist nbpass; } // coût public String tostring () { return "4x4" ; } // tostring } // QuatreQuatre package transports; / MonoSpace.java public class MonoSpace extends Voiture { public double coût (int nbpass, double dist ) { return 11 dist nbpass; } // coût public String tostring () { return "Monospace" ; } // tostring } // MonoSpace package transports; / SideCar.java public class SideCar extends Moto { public double coût (int nbpass, double dist ) { return 4 dist nbpass; } // coût public String tostring () { return "SideCar" ; } // tostring } // SideCar
MODULE TransportsConcretExtExt; TransportsConcretExt, ZStrPlus; TYPE SUV = POINTER TO EXTENSIBLE RECORD (TransportsConcretExt.QuatreQuatre) PROCEDURE (s: SUV) Coût (nbpass: INTEGER; dist: REAL): REAL, EXTENSIBLE; BEGIN (Coût) RETURN 21distnbPass; END Coût; PROCEDURE (s: SUV) ToString (): POINTER TO ARRAY OF CHAR, EXTENSIBLE; BEGIN (ToString) RETURN ZStrPlus.DynStr("SUV") END ToString; END TransportsConcretExtExt.
package transports; / SUV.java public class SUV extends QuatreQuatre { public double coût (int nbpass, double dist ) { return 21 dist nbpass; } // coût public String tostring () { return "SUV" ; } // tostring } // SUV
MODULE TransportsClient0; Out, TransportsAbstrait, TransportsConcret; PROCEDURE Do; VAR voit: TransportsConcret.Voiture; moto: TransportsConcret.Moto; pass: TransportsConcret.Passager; fret: TransportsConcret.Fret; véh: TransportsAbstrait.Véhicule; ( Il est possible de déclarer une référence à un type abstrait. ) chose: TransportsAbstrait.ChoseTransportée; ( Ditto. ) BEGIN (Do) NEW(voit); NEW(moto); ( Méditer l'erreur déclenchée par le verso de ce Fold. ) Out.Open; Out.Ln; Out.String("Le véhicule voit est de type "+voit.tostring()); Out.Ln; Out.String("Le véhicule moto est de type "+moto.tostring()); NEW(pass); NEW(fret); ( Méditer l'erreur déclenchée par le verso de ce Fold. ) pass.ident := "Charles-Henry";( Attention: chapeau rose!!!! ) fret.ident := "valises"; Out.Ln; Out.String("Le passager est "+pass.tostring()); Out.Ln; Out.String("Le fret est "+fret.tostring()); END Do; END TransportsClient0.! TransportsClient0.Do Erreur : L'erreur est bien sûr qu'on ne peut pas allouer des types ABSTRACTs. Le POINTER lui peut être alloué automatiquement (la déclaration locale), ce qu'on ne peut pas faire, c'est allouer la zone pointée. MODULE TransportsClient0; Out, TransportsAbstrait, TransportsConcret; PROCEDURE Do; VAR voit: TransportsConcret.Voiture; moto: TransportsConcret.Moto; pass: TransportsConcret.Passager; fret: TransportsConcret.Fret; véh: TransportsAbstrait.Véhicule; ( Il est possible de déclarer une référence à un type abstrait. ) chose: TransportsAbstrait.ChoseTransportée; ( Ditto. ) BEGIN (Do) NEW(voit); NEW(moto); NEW(véh); ( Méditer l'erreur déclenchée. ) Out.Open; Out.Ln; Out.String("Le véhicule voit est de type "+voit.tostring()); Out.Ln; Out.String("Le véhicule moto est de type "+moto.tostring()); NEW(pass); NEW(fret); NEW(chose); ( Méditer l'erreur déclenchée. ) pass.ident := "Charles-Henry";( Attention: chapeau rose!!!! ) fret.ident := "valises"; Out.Ln; Out.String("Le passager est "+pass.tostring()); Out.Ln; Out.String("Le fret est "+fret.tostring()); END Do; END TransportsClient0.
/ Client0.java import transports. ; public class Client0 { / Traduction avec les déclarations de variables groupées au début de la méthode, comme en Pascal public static void main0 (String [] args ) { Voiture voit; Moto moto; Passager pass; Fret fret; Véhicule véh; ChoseTransportée chose; voit = new Voiture (); moto = new Moto (); //véh = new Véhicule(); // Méditer l'erreur déclenchée System.out. println (); System.out. println ("Le véhicule voit est de type " +voit. tostring ()); System.out. println ("Le véhicule moto est de type " +moto. tostring ()); pass = new Passager (); fret = new Fret (); //chose = new ChoseTransportée(); // Méditer l'erreur déclenchée pass.ident = "Charles-Henry" ; fret.ident = "valises" ; System.out. println (); System.out. println ("Le passager est " +pass. tostring ()); System.out. println ("Le fret est " + fret. tostring ()); } // main0 / Le standard Java est respecté: les variables sont déclarées au fur et à mesure de leur emploi public static void main (String [] args ) { Voiture voit = new Voiture (); Moto moto = new Moto (); //Véhicule véh = new Véhicule(); // Méditer l'erreur déclenchée System.out. println (); System.out. println ("Le véhicule voit est de type " +voit. tostring ()); System.out. println ("Le véhicule moto est de type " +moto. tostring ()); Passager pass = new Passager (); Fret fret = new Fret (); //ChoseTransportée chose = new ChoseTransportée(); // Méditer l'erreur pass.ident = "Charles-Henry" ; fret.ident = "valises" ; System.out. println (); System.out. println ("Le passager est " +pass. tostring ()); System.out. println ("Le fret est " + fret. tostring ()); } // main } // Client0
MODULE TransportsClient10; Out, TransportsAbstrait, TransportsConcret; PROCEDURE Do; VAR voit: TransportsConcret.Voiture; moto: TransportsConcret.Moto; véh: TransportsAbstrait.Véhicule; BEGIN (Do) NEW(voit); NEW(moto); Out.Open; véh := voit; Out.Ln; Out.String("Le véhicule est de type "+véh.tostring()); voit.nbportes := 4; ( Méditer l'erreur déclenchée par le verso de ce Fold. ) véh := moto; Out.Ln; Out.String("Le véhicule est de type "+véh.tostring()); END Do; END TransportsClient10.! TransportsClient10.Do La liaison entre le symbole "véh.tostring()" et la bonne implantation de la méthode ToString() ne peut évidemment pas se faire à la compilation (voir TransportsClient11). Elle se fait au moment de l'exécution, car c'est uniquement à ce moment-là que le type effectivement référencé est connu : il s'agit de la liaison dynamique. MODULE TransportsClient11; In, Out, TransportsAbstrait, TransportsConcret; PROCEDURE Do; VAR voit: TransportsConcret.Voiture; moto: TransportsConcret.Moto; véh: TransportsAbstrait.Véhicule; n: INTEGER; BEGIN (Do) NEW(voit); NEW(moto); In.Open; In.Int(n); Out.Open; IFn>0THENvéh := voit ELSE véh := moto Out.Ln; Out.String("Le véhicule est de type "+véh.tostring()); END Do; END TransportsClient11.! TransportsClient11.Do -1 1 Remarques : Ici, c'est clairement à l'exécution que véh prend un type ou l'autre. La liaison avec la méthode ne peut évidemment pas se faire lors de la compilation. Ici, l'impossibilité est rendue évidente par le fait qu'il y a une saisie.
/ Client10.java import transports. ; public class Client10 { public static void main (String [] args ) { Voiture voit = new Voiture (); Moto moto = new Moto (); Véhicule véh = voit; System.out. println ("Le véhicule est de type " + véh. tostring ()); //véh.nbportes = 4; // Méditer l'erreur déclenchée par cette instruction voit.nbportes = 4; véh = moto; System.out. println ("Le véhicule est de type " + véh. tostring ()); } // main } // Client10 / Client11.java import transports. ; public class Client11 { public static void main (String [] args ) { Voiture voit = new Voiture (); Moto moto = new Moto (); int n = Integer.parseInt (args [0]); Véhicule véh; if (n > 0) véh = voit; else véh = moto; System.out. println ("Le véhicule est de type " + véh. tostring ()); } // main } // Client11
MODULE TransportsClient20; Out, TransportsAbstrait, TransportsConcret; PROCEDURE Do; VAR voit: TransportsConcret.Voiture; moto: TransportsConcret.Moto; PROCEDURE Print (véh: TransportsAbstrait.Véhicule); ( Procédure générique d'impression d'un véhicule. ) BEGIN (Print) Out.Ln; Out.String("Le véhicule est de type "+véh.tostring()) END Print; BEGIN (Do) NEW(voit); NEW(moto); Out.Open; Print(voit); Print(moto) END Do; END TransportsClient20. C'est dans cet exemple que la projection prend tout son sens : en effet, celle-ci a lieu lors du passage de paramètres. Et donc, la procédure Print est capable de fonctionner pour n'importe quel véhicule ; le type statique de véh sera toujours Véhicule alors que sont type dynamique sera défini lors de l'appel (d'abord Voiture, puis Moto).
/ Client20.java import transports. ; public class Client20 { private static void print (Véhicule véh ) { System.out. println ("Le véhicule est de type " + véh. tostring ()); } // print public static void main (String [] args ) { Voiture voit = new Voiture (); Moto moto = new Moto (); print (voit ); print (moto ); } // main } // Client20
MODULE TransportsPrintVéhicule; Out, TransportsAbstrait; PROCEDURE Print (véh: TransportsAbstrait.Véhicule); ( Procédure générique d'impression d'un véhicule. ) BEGIN (Print) Out.Ln; Out.String("Le véhicule est de type "+véh.tostring()) END Print; END TransportsPrintVéhicule. C'est une procédure générique. Elle est capable d'afficher le string de n'importe quel Véhicule. En particulier, on pourra l'employer avec des classes dérivées de Véhicule non encore connues au moment de la compilation de ce module. MODULE TransportsClient21; Out, TransportsConcret, TransportsPrintVéhicule; PROCEDURE Do; VAR voit: TransportsConcret.Voiture; moto: TransportsConcret.Moto; BEGIN (Do) NEW(voit); NEW(moto); Out.Open; TransportsPrintVéhicule.Print(voit); TransportsPrintVéhicule.Print(moto); END Do; END TransportsClient21.
package transports; / PrintVéhicule.java public abstract class PrintVéhicule { public static final void print (Véhicule véh ) { System.out. println ("Le véhicule est de type " + véh. tostring ()); } // print } // PrintVéhicule / Client21.java import transports. ; public class Client21 { public static void main (String [] args ) { Voiture voit = new Voiture (); Moto moto = new Moto (); PrintVéhicule. print (voit ); PrintVéhicule. print (moto ); } // main } // Client20
MODULE TransportsConcretPlus; TransportsAbstrait, ZStrPlus; TYPE Bateau = POINTER TO RECORD (TransportsAbstrait.Véhicule) PROCEDURE (b: Bateau) Coût (nbpass: INTEGER; dist: REAL): REAL; BEGIN (Coût) RETURN 12distnbPass; ( Pourrait être affiné, ce n'est pas le but ici. ) END Coût; PROCEDURE (b: Bateau) ToString (): POINTER TO ARRAY OF CHAR; BEGIN (ToString) RETURN ZStrPlus.DynStr("Bateau") END ToString; END TransportsConcretPlus. Classe créée après compilation de TransportsPrintVéhicule (pour lequel le source pourrait même être perdu). MODULE TransportsClientPlus; Out, TransportsConcretPlus, TransportsPrintVéhicule; PROCEDURE Do; VAR bateau: TransportsConcretPlus.Bateau; BEGIN (Do) NEW(bateau); Out.Open; TransportsPrintVéhicule.Print(bateau); END Do; END TransportsClientPlus.
package transports; / Bateau.java public final class Bateau extends Véhicule { public double coût (int nbpass, double dist ) { return 12 dist nbpass; } // coût public String tostring () { return "Bateau" ; } // tostring } // Bateau / ClientPlus.java import transports. ; public class ClientPlus { public static void main (String [] args ) { Bateau bateau = new Bateau (); PrintVéhicule. print (bateau ); } // main } // ClientPlus