First NIT release and new clean mercurial repository
[nit.git] / tests / example_objet.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Exemple commenté d'un programme en NIT
18 # Cet exemple est tiré du dernier TP C++ de l'année des IUP1
19
20 # Attention, NIT est un langage enrichi en sucre (syntaxique) et en déclaration
21 # implicite. Cet exemple en introduit petit à petit. Et chaque utilisation est
22 # abondamment commentée. D'ailleurs, a propos de commentaires, il est peut-être
23 # trop tard pour dire que ceux-ci commencent par un dièse (#)
24
25 # Les points suivants sont couverts par cet exemple :
26 # - définition des modules
27 # - définition des classes
28 # - définition des propriétés (attributs, procédures, fonctions)
29 # - structures de contrôles (blocs, boucles, conditions)
30 # - variables locales
31 # - accesseurs universels
32 # - constructeurs et constructions d'instances
33 # - chaînes de caractères
34
35 # Les points suivants sont aperçus :
36 # - classes génériques
37 # - protection (visibilité des propriétés)
38 # - collections
39 # - héritage et redéfinition
40
41 # Les points suivants ne sont pas abordés :
42 # - accesseurs implicites
43 # - (re)définition des opérateurs
44 # - paramètres implicites
45 # - méthodes à arité variable
46 # - méthodes retardées (virtuelles pures)
47 # - constructeurs universels
48 # - appel à super
49 # - héritage multiple
50 # - covariance des types
51 # - raffinement de classes
52
53 # Un fichier = un module. Les classes d'un module sont définies dans le fichier.
54 # Au début des fichiers, les modules à importer doivent être déclarés (via le
55 # mot clé "import"). Implicitement, tout module importe le module nommé
56 # "standard" qui définit les classes usuelles.
57
58 # On va étudier une représentation d'un entrepôt de produits variés.
59 class Entrepot
60 private
61 # Dans un bloc "private", les propriétés déclarées sont seulement accessibles au
62 # receveur ("self")
63
64 # Les attributs sont déclarés par le mot clé "meth" et commencent par un "@"
65 attr _nom_: String # Un entrepôt a un nom (de type chaîne).
66 attr _rayons: Array[Rayon] # Il est composé d'un ensemble de rayon.
67 # "Array" est une classe paramétrée, les crochets en sont la marque.
68 # La classe "Rayon" est définie plus loin
69
70
71 # Les propriétés déclarées dans un bloc "public" sont accessibles à tout le
72 # monde.
73
74 # Les méthodes (fonctions et procédures) sont déclarées par le mot clé "meth"
75 # nom est une fonction qui retourne une chaîne
76 meth nom: String
77 do
78 # "return" sort de la fonction
79 return _nom_
80 # En fait, les attributs et les méthodes sont dans deux espaces
81 # de noms distincts.
82 # Le @ peut se prononcer "at" ce qui rappelle le mot attribut
83 end
84
85 # set_nom est une procédure qui prend une chaîne en paramètre
86 meth set_nom(n: String)
87 do
88 _nom_ = n # "=" désigne l'affectation
89 # Les affectations sont des instructions et ne sont pas
90 # chaînées
91 end
92
93 meth add_rayon(r: Rayon)
94 do
95 _rayons.add(r) # "add" ajoute un élément
96 end
97
98 # to_s est la méthode implicitement appelée par "print" et "println"
99 # Il s'agit de la représentation "humaine" d'un objet
100 redef meth to_s: String
101 do
102 # Les variables sont déclarées par "var", leur portée va de leur
103 # déclaration jusqu'au "end" correspondant
104 var s: String # Là où on calcule le résultat
105 # Les chaînes littérales sont déclarées avec des guillemets
106 s = "*** Entrepôt " # On initialise "s"
107 # puis on concatène des chaînes à "s"
108 s.append(_nom_) # la méthode "append" concatène
109 s.append(" ***\n")
110 # Les conditions sont de la forme "if then else if else end"
111 if _rayons.is_empty then
112 s.append("L'entrepôt est vide\n")
113 else
114 var i: Iterator[Rayon]
115 # Les itérateurs permettent de traverser les collections
116 i = _rayons.iterator # "iterator" retourne un nouvel itérateur
117 # La forme des boucles est "while do end"
118 while i.is_ok do # on pointe un élément valide ?
119 # "item" retourne l'élément pointé par
120 # un iterateur
121 s.append(i.item.to_s)
122 i.next # passe à l'élément suivant
123 end
124 s.add('\n') # "add" ajoute un caractère à la fin.
125 # Comme en C, les caractères sont entre simples cotes.
126 end
127 return s
128 end
129
130
131 # Dans un bloc "constructor", les méthodes déclarées sont celles utilisées pour
132 # créer des instances
133
134 # init sans paramètre est le constructeur implicite
135 init
136 do
137 _nom_ = "sans nom"
138 # "new" permet d'instancier une classe.
139 _rayons = new Array[Rayon]
140 # Un nouveau tableau est implicitement vide
141 # On aurait pu prévoir une capacité en utilisant un autre
142 # constructeur et en écrivant :
143 # _rayons = new Array[Rayon].with_capacity(50)
144 end
145
146 # Plusieurs constructeurs peuvent être définis
147 init with_nom(n: String)
148 do
149 _nom_ = n
150 _rayons = new Array[Rayon]
151 end
152
153 # Une classe qui ne possède pas de méthode définie dans un bloc "constructor"
154 # est une classe abstraite : elle ne peut être instanciée.
155 end
156
157 class Produit
158 private
159 attr _nom_: String # Désignation du produit
160 attr _qte_: Int # Quantité en stock
161
162
163
164 # Comme nous l'avons vu, les accesseurs en lecture sont
165 # généralement homonymes aux attributs (au @ près).
166 meth nom: String
167 do
168 return _nom_
169 end
170
171 meth qte: Int
172 do
173 return _qte_
174 end
175
176 # Toutefois, pour les accesseurs en écriture, il est d'usage en NIT
177 # d'utiliser un type de méthode particulier appelé "accesseur universel"
178 # son nom se caractérise par un "=" final
179 meth qte=(i: Int)
180 do
181 _qte_ = i
182 end
183 # La méthode s'utilise alors de façon élégante :
184 # pour p de type statique Produit, l'expression
185 # p.qte
186 # correspond à un envoi de message vers la fonction "qte" et retourne
187 # la quantité d'un produit. Tandis que l'instruction
188 # p.qte = 16
189 # correspond à un envoi de message vers la méthode "qte=" et affecte
190 # la quantité d'un produit.
191
192
193 redef meth to_s: String
194 do
195 # On peut aussi utiliser "+" pour concaténer les chaînes
196 return _nom_ + ":" + _qte_.to_s
197 # Mais ce n'est pas très efficace car cela crée des objets
198 # intermédiaires.
199 end
200
201
202 init(nom: String, qte: Int)
203 do
204 _nom_ = nom
205 _qte_ = qte
206 end
207 end
208
209 class Rayon
210 private
211 attr _stock: Array[Produit] # Des produits en stock
212 attr _rubrique: String # La catégorie des produits stockés
213
214 # Cette fonction est utilisé par to_s pour afficher un petit titre
215 meth to_s_head: String
216 do
217 # Les déclarations de type sont optionnelles dans un bloc "var".
218 # Si une expression est passée comme valeur initiale d'une
219 # variable, le type statique de la variable est implicitement
220 # celui de l'expression.
221 var s = "* Rayon : "
222 # Ici, le type statique de s est implicitement String
223
224 s.append(_rubrique)
225 s.add('\n')
226 return s
227 end
228
229 meth cherche_produit(n: String): Produit
230 do
231 var i = _stock.iterator
232 while i.is_ok do
233 var p = i.item
234 # "=" est l'opérateur égalité de valeur.
235 if p.nom == n then
236 return p
237 end
238 i.next
239 end
240 # "null" correspond à l'objet vide
241 return null
242 end
243
244
245 redef meth to_s: String
246 do
247 var s = to_s_head
248 # Les boucles en NIT sont des structures puissantes, toutefois
249 # la manipulation des itérateurs peut être facilité par la
250 # structure "for in"
251 for p in _stock do
252 # L'expression de droite doit être sous-type de Collection
253 # (ce qui est le cas pour Array[Produit]).
254 # La variable p est automatiquement déclarée, son type
255 # automatiquement déduit (ici Produit) et la collection est
256 # automatiquement parcourue
257 # En réalité, la structure "for in" n'est que du sucre
258 # syntaxique pour une boucle sur une collection via des
259 # itérateur comme celle écrite dans la fonction
260 # "cherche_produit" précédente
261 s.append(p.to_s)
262 s.add('\n')
263 end
264 return s
265 end
266
267
268 # Cette fonction permet de retourner la quantité d'un produit donné
269 # Ce service est très simple à implémenter si on utilise cherche_produit
270 meth quantite(n: String): Int
271 do
272 var p = cherche_produit(n)
273 # "==" est l'opérateur d'égalité d'identité
274 if p == null then
275 return 0
276 else
277 return p.qte
278 end
279 end
280
281 # Le service dual consiste à définir la quantité d'un produit
282 # En NIT, il est d'usage de définir ce genre de service par un accesseur
283 # universel :
284 meth quantite=(n: String, i: Int)
285 do
286 var p = cherche_produit(n)
287 if p == null then
288 # On Construit un nouveau produit que l'on ajoute
289 _stock.add(new Produit(n, i))
290 else
291 # On change la quantité du produit trouvé
292 p.qte = i
293 end
294 end
295 # Les accesseurs universels s'utilise de façon toujours aussi élégante.
296 # Pour r de type Rayon, on écrira donc :
297 # r.quantite("clous")
298 # pour obtenir la quantité de clous dans le rayon, et :
299 # r.quantite("clous") = 15
300 # pour mettre le nombre de clous à 15
301 end
302
303 class RayonNormal
304 special Rayon
305
306 init(r: String)
307 do
308 _rubrique = r
309 _stock = new Array[Produit]
310 end
311 end
312
313 class RayonFroid
314 # Les super-classes sont déclarés avec le mot clé "special".
315 # Implicitement, c'est "Object", la classe de tous les objets.
316 special Rayon
317
318 # Tant qu'on parle d'implicite, en l'absence de bloc de propriétés,
319 # celles-ci sont déclarées en tant que "public"
320 meth temp_max: Int
321 do
322 return _temp_max_ # Attribut défini juste en dessous
323 end
324
325 attr _temp_max_: Int # Une température maximale
326 # Les autres attributs sont bien sûr hérités !
327
328 redef meth to_s_head: String # On redéfinit cette méthode
329 do
330 # nous avons vu le "append" et le "+" sur les chaînes, mais la
331 # méthode la plus simple reste la construction "{}" qui
332 # permet l'inclusion automatique d'expressions. C'est aussi la méthode
333 # la plus efficace.
334 return "* Rayon Réfrigéré : {_rubrique} - t° max : {_temp_max_}\n"
335 end
336
337
338 init(r: String, t: Int)
339 do
340 _temp_max_ = t
341 _rubrique = r
342 _stock = new Array[Produit]
343 end
344 end
345
346
347
348 # Le main (point d'entrée du programme) peut être déclaré directement à la fin
349 # des définitions de classes
350
351 # Constructeur implicite ("init")
352 var e = new Entrepot
353 # "println" affiche un truc et ajoute un \n à la fin
354 print(e.nom) # affiche "sans nom"
355 # "print" affiche un truc mais sans le \n à la fin
356 printn('\n') # va à la de ligne
357
358 e.set_nom("Montpellier")
359 printn(e.nom, "\n\n")
360 # Affiche "Montpellier" et saute une ligne.
361 # Contrairement à "println", "print" peut prendre plusieurs paramètres
362
363 # Constructeur explicite ("with_nom")
364 e = new Entrepot.with_nom("Lunel") # on perd l'ancien entrepôt
365 printn(e, '\n')
366 # affiche "*** Entrepot Lunel *** L'entrepôt est vide"
367
368 var p = new Produit("Carotte", 15)
369 print(p) # affiche Carotte:15
370 p.qte = 20
371 print(p) # affiche Carotte:20
372 printn('\n')
373
374 var r = new RayonNormal("Légumes")
375 printn(r, '\n')
376
377 r.quantite("Carotte") = 15
378 r.quantite("Navet") = 10
379 r.quantite("Chou") = 3
380 printn(r)
381 r.quantite("Chou") = 13
382 r.quantite("Courge") = 1
383 printn(r, '\n')
384
385 e.add_rayon(r)
386
387 var r2 = new RayonFroid("Surgelés", -5)
388 e.add_rayon(r2)
389 r2.quantite("Pizza") = 12
390 r2.quantite("Poisson pané") = 4
391 printn(e)