Cours préparé par Dr Philippe Martin
http://www.phmartin.info/cours/GL/
Partie 0. Définitions, critères, principes de présentation informelle 0.0. Cas d'utilisation "UseCaseX" 0.1. Définitions générales : ..., paradigmes de programmation, ..., ordonnancement des tâches de développement (cycle de vie) 0.2. Critères fonctionnels/structurels de qualité logicielle (les "principes de conception" et le choix de langage (modèle + présentation formelle) sont dans la partie 1 du cours) 0.3. Quelques principes pour la présentation informelle ... 0.3.1. ... de l'interface textuelle/graphique (pour satisfaire "au mieux" le critère d'utilisabilité, à propos de l'IHM) : règles pour l'IHM citées dans le cours de coopération 0.3.2. ... du code (pour satisfaire "au mieux" le critère de lisibilité, sous-critère de maintenabilité) : 0.3.2.1. Style de programmation ; règle générale ; commentaires ; conventions de nommage ; indentation ; tests et messages d'entrée-sorties 0.3.2.2. Approches plus génériques et paramétrables
Voici le cas d'utilisation
[use case]
qui sera exploité dans ce cours.
Il sera référé sous le nom de UseCaseX.
Vous êtes employés de la
SSII
(Société de Services en Ingénierie Informatique) "SS2IX".
Pour des raisons économiques (entre autres), SS2IX souhaite réutiliser
au maximum
les composants logiciels qu'elle développe.
ClientX est un de ses clients réguliers.
Comme beaucoup de clients, il ne connaît (ou ne sait exprimer) ses besoins
qu'approximativement et ses besoins peuvent évoluer.
ClientX vient vous voir pour demander un logiciel - que nous appellerons logicielX -
ayant au moins la fonctionnalité suivante.
Xfct1 :
pour toutes les informations dans un ordinateur ou dans un répertoire ou,
parmi ces informations,
celles d'un certain média (e.g., textuel, image, ...) ou
celles identifiées par un certain nom (e.g., celles dont le nom finit par ".bmp")
- Xfct1.1 : afficher le nombre d'octets (nbBytes), de mots (nbWords) et
de lignes (nbLines) de ces informations ;
- Xfct1.2 : afficher "tous les mots ou certains des mots"
(e.g., le mot à la position positionOfTheWordToDisplay)
des lignes qui ont un certain mot (wordX)
plus d'un certain nombre de fois (maxNbWordXByLine) dans la ligne.
Si le nombre de ces lignes est inférieur à un certain nombre
(maxNbLinesWith_wordX ;
ClientX dit que ce nombre ne dépassera pas 100);
à la fin (que des lignes aient été affichées ou pas),
afficher
le nombre des lignes répondant aux critères
(nbLinesWith_maxNbWordXByLine) ;
- Xfct1.3 : faire en sorte que les moyennes (sur l'ensemble des informations) des
variables nbBytes, nbWords, nbLines et nbLinesWith_maxNbWordXByLine
puissent être affichées ;
- Xfct1.4 (new) : effectuer un search&replace
(en une seule commande).
Exercice oral : quelles précisions demanderiez-vous à ClientX ?
Exemples de questions de haut niveau : quelles sont les contraintes/variables financières, temporelles, logicielles (système d'exploitation, langage, temps de réponse, ...), algorithmiques (ordre de parcours des répertoires, liens symboliques, ...), de format pour les entrées et sorties (textuelle, graphiques, ...), etc. Réponse à prendre en compte pour l'exercice ci-dessous : les programmes doivent être indépendants du système d'exploitation.
Exercices à effectuer (-> programmer, tester avec "fenêtre d'erreur" ouverte,
documenter le source, ...) puis e-mailer à l'enseignant avant le début du
prochain cours (exercices non rendus ou incomplètement effectués -> 0/20).
Version finale à rendre au plus tard mardi 18/3/2014 midi
(1 fichier source par exo → 3 pièces jointes à votre e-mail, pas de .tar ni de .h ;
ces exercices sont très courts, ils ne nécessitent pas de .h ;
de plus, conserver leur lisibilité en les organisant en différentes sections via
des commentaires est un bon exercice ;
les règles des 5 pages de la section 0.3.2.1 doivent toutes être suivies ;
si vous utilisez du C, la fonction main() doit simplement décoder les
paramètres
et appeler une fonction générique ; l'entête de
chaque fichier doit entre autre
inclure un exemple de ligne de commande pour
compiler votre programme et
un exemple de ligne de commande pour tester votre
programme) :
Pour jeudi 27/2/2014, 10:00 :
Possibles solutions du dernier exercice,
tout d'abord en C++ avec les itérateurs C++
puis en C (commme demandé).
template<typename Thing> class Container { public std::vector<Thing> items; Container (const char *s) { while (s++) items.push_back(*s); } } template<typename Thing> int nbElements (const Container &c) { int n= 0; for (std::vector<Thing>::iterator iter= c.items.begin(); //-> iter points to an element of the container if it not empty iter != c.items.end(); //-> an element of the container is pointed iter++ //the equivalent with a linked list in C would be : iter= iter->next(); ) n++; return n; } ContainersomeCharContainer("abcd"); //copy of "abcd" into this container int n= nbElements(someCharContainer); //note: for your code in C, to make the above copy, a function named // "makeContainerFromString" may be used ; you then have to implement // this function ; your container may "use" a linked list
struct Container { LinkedList items; } int nbElements (const Container &c) { int n= 0; for ( ; c.items; c.items= c.items->next()) n++; return n; } Container makeContainerFromString (const char *s) {...} int n= nbElements(someCharContainer);
E.g.
(latin: exempli gratia) : "par exemple".
I.e. (latin: id est) :
"c'est-à-dire".
Vs. : "versus".
/ : "ou". // : "ou bien".
| : (quasi-)"alias" (entre 2 termes, dans le contexte où
ils sont définis).
Informatique
[computer science,
Information Technology (IT),
computing,
electronic data processing
] :
domaine/activité lié(e) au traitement automatique
de l'information par des machines.
Technologies de l'Information et de la Communication (TIC)
[Information and
Communication Technologies (ICT)] :
techniques utilisées dans le traitement et
la transmission des informations,
principalement de l'informatique, de l'Internet et des
télécommunications.
"TIC" est plus général que "Informatique"
mais "ICT" moins général que "Computing"
(-> le "et" ci-dessus est interprété différemment).
Information : données ou bien (représentations de) connaissances.
Connaissances [knowledge] : information organisée par des
relations sémantiques
(toute relation entre des sens de mots, e.g., la relation de
spécialisation,
sous-partie, localisation spatiale/temporelle, ...)
dans un formalisme logique,
ce qui permet à un logiciel de les exploiter
pour effectuer des inférences logiques.
Données [data] : information non
organisée ou de manière non "explicite",
i.e., non formellement ou sans relation sémantique.
Exercice (de contrôle ;
après réponse aux questions éventuelles des étudiants) :
- Un schéma de base de données est-il une représentation de
connaissances ?
Généralement non car un schéma ne représente généralement pas de relations sémantiques entre les objets (e.g., entre ceux représentés par des tables et ceux représentés dans leurs colonnes).
Système informatique
[information system
(domain: Information Systems)] :
système aidant la réalisation de tâches/décisions et
composé de
matériels [hardware] (ordinateur, périphériques, ...),
logiciels [software],
information et personnes.
Logiciel [software] : (ensemble de) programme(s) et informations associées.
Programme (informatique)
[computer program] :
suite de "phrases" (instructions ou descriptions)
[statements]
écrites dans un language de programmation (et/ou de modélisation),
et donc implémentant des
algorithmes
[algorithms],
i.e., des méthodes permettant
de calculer des
fonctions ou bien des procédures.
Les instructions|structures de contrôle
définissent (totalement ou partiellement)
l'ordre d'exécution d'autres instructions.
Progiciel : mot-valise,
contraction de "produit" et "logiciel", référant à un
logiciel applicatif, libre ou propriétaire,
"prêt-à-porter", standardisé et générique,
prévu pour répondre à des besoins ordinaires.
Language de programmation
[programming language] :
langage formel (permettant
d'écrire des programmes), donc ayant
- un modèle de langage (alias, ontologie de langage, i.e.,
un alphabet|vocabulaire, avec des définitions ou des règles de
composition,
formant une structure abstraite ayant une interprétation/sémantique
associée,
et suivant un paradigme de programmation),
- une présentation formelle (alias, structure concrète, syntaxe,
notation,
avec un
alphabet|vocabulaire de symbôles/mots
et une grammaire formelle).
Analyse (effectuée par un interpréteur ou compilateur;
cf. définitions ci-dessous) :
- analyse lexicale
- préprocessing
- analyse syntaxique [parsing]
- analyse sémantique
- génération de code.
Interpréteur
[interpreter] :
outil analysant et traduisant ou exécutant des programmes.
À la différence d'un compilateur (cf. définition ci-dessous),
un interpréteur n'effectue pas
une analyse sémantique (cf. définition ci-dessus) une fois pour toute,
il l'effectue seulement
pour les parties de code qu'il exécute
(cela dépend donc des données en entrée) et
à chaque fois qu'il les exécute.
Langage machine : suite de bits
représentant un programme et donc destinée à être
interprété par le processeur d'un ordinateur.
Langage machine virtuel, langage pour machine virtuelle, code octet [byte code] :
code (généralement binaire) exécuté par une
machine virtuelle, i.e., par
un programme
émulant les principales fonctionnalités d'un ordinateur.
Compilateur
[compiler] : programme qui,
à partir d'un code écrit dans un langage de programmation
créé un code équivalent mais écrit dans un langage plus facilement
exécutable,
typiquement un langage machine, un langage d'assemblage ou du code octet.
Généricité dans la programmation
[genericity in programming] :
usage de variables (et donc
d'entités de 1er ordre)
pour tout ce qui peut changer
dans le processus qui est programmé (i.e., qui est l'object de la programmation).
Choses liées à un processus (→ choses qui peuvent changer pour un processus ;
pour un schéma plus détaillé, cliquez ici) :
Time | Place | Process | State (when, where, how long, before/after what, why/purpose/because) ↗ /time | place | from/to/before/after/during place/time/process | volume | ... / / / param | in | ... | out Process ------------------------> Data parameter/input/material/instrument/output | \ ("what, on/to/with what, how" for data) | \ | \ | \sub/embedding-process | method | specializing/generalizing_process | ↘ | Process | Description ("what, on/to/with what, how" for code) | (-> data/control structures/types, functions de 1er ordre) | | |agent/initiator | experiencer/recipient V Participant (who)
Paradigmes de programmation
[programming paradigms] :
styles de
programmation, fondés sur des "modèles de traitement"
[computation models]
différents. Les deux plus importants sont :
* La programmation impérative
(e.g., procédurale ou orientée-objet,
comme avec C, Java, PHP, ...) qui est fondée sur le modèle de la
machine de Turing
et qui décrit les traitements via des successions de "commandes". Celles-ci
"effectuent directement" - plutôt que "décrivent" - des changements sur des
états de ressources partagées (fichier, variables globales, ...) et ont
donc souvent
des effets de bord : appels
de commande/procédure identiques conduisant à des
résultats
différents.
* La programmation déclarative où ce sont les résultats
désirés qui doivent être
spécifiés plutôt
que la manière d'y parvenir : pas ou peu de contraintes peuvent être
spécifiées pour l'ordre des étapes|instructions|commandes
à effectuer, et les
changements d'états soit ne sont pas décrits (et sont donc gérés par
l'interpréteur)
soit impliquent de décrire et relier des contextes différents. E.g. :
- la programmation logique (i.e., basée sur une logique),
e.g., avec Prolog, KIF, ...
- la programmation fonctionnelle (i.e., basée sur la composition de
fonctions,
de préférence "pures", i.e., "sans état" :
sans modification de ressources partagées) ;
e.g., avec LISP, ML, Haskell, ...
- l'usage expressions régulières,
- l'usage de langages de requêtes (e.g., SQL, QUERY, SPARQL, ...), etc.
Pour culture, lisez ces pages :
- Comparison of programming paradigms
- LISP,
Standard ML,
Haskell,
Prolog,
Forth et
d'autres langages de programmation plus "ésotériques".
Exercice :
a. Les langages de description de formats (XML, ...) sont-ils déclaratifs ?
b. Sont-ils des langages de programmation ?
c. Permettent-ils des représentations de connaissances ?
d. Un programme C représente-t-il des connaissances ?
e. Un programme dans un langage logique représente-t-il des connaissances ?
f. Comme dans les exercices et exposés de
votre cours de coopération
au semestre précédent,
et donc conformément aux règles de bases rappelées page suivante, reliez
(graphiquement et dans le langage FL) par des relations ">" (sous-type) et "part"
les différents types de paradigme et de langage de programmation référés
précédemment.
a. Oui b. Non car ils ne peuvent pas être utilisés directement pour implémenter des algorithmes. Toutefois, des langages tels XML peuvent être utilisés pour rendre explicite et formatter des structures abstraites de langages et peuvent donc être utilisés pour définir des structures concrètes de langages. c. Uniquement si utilisé pour encoder des structures concrètes de langages de représentation de connaissances. d. Non. Une hiérarchie de classes dans un programme C++ ou Java est toutefois une forme très limitée de représentation de connaissances. e. Oui, de manière plus explicite|organisée suivant les programmes. f. paradigme_de_programmation //(le type) paradigme_de_programmation > (programmation_impérative // a pour sous-type programmation_impérative qui > (programmation_procédurale // a pour sous-type programmation_procédurale langage: 1..* (. langage_de_programmation_procédurale > C PHP) ) //=> toute programmation_procédurale a pour langage 1 à plusieurs instances du // type langage_de_programmation_procédurale, lequel a pour sous-types // C et PHP (entre autres) (programmation_orientée_objet alias: POO, langage: 1..* (. langage_de_programmation_procédurale_permettant_la_POO > Java (C++ partie: 1..* C) ) ) ) (programmation_déclarative //autre sous-type direct de paradigme_de_programmation > (programmation_logique langage: 1..* (. langage_de_programmation_logique > Prolog KIF) ) (programmation_fonctionnelle langage: 1..* (. langage_de_programmation_fontionnelle > LISP ML Haskell ) ) );
Règles de base pour la représentation de connaissances
(règles à suivre dans tous vos exercices de modélisation ;
ce sont les règles déjà vues au début de la section 2.6 du cours de coopération) :
1. Une relation binaire de type *rt (e.g., 'subtype' or 'part')
depuis un nœud source *s (e.g., 'feline' or 'at least 80% of car')
vers une destination *d (e.g., 'cat' or 'at most 100 wheel') se lit :
" *s has/have for *r *d ". E.g. :
`feline > cat´ (i.e., `feline subtype: cat´) se lit
"feline has for subtype cat"
(ou "the type feline has for subtype the type cat"),
`at least 80% of car part: at most 100 wheel´ se lit
"at least 80% of cars have for part at most 100 wheels".
Ce dernier exemple peut aussi se lire :
"at least 80% of instances of the
type car have for part
at most 100 instances of the type wheel". Enfin,
conformément à la règle 7 ci-dessous,
`car part: at most 100 wheel´ se lit
"any (instance of) car has for part at most 100 (instance of) wheel(s)".
2. Si *r est suivi de "of" (pour inverser la direction de la relation), il vaut mieux lire
" *s is/are *r of *d ".
E.g., `cat < feline´ (i.e., `feline subtype of: cat´) se lit
"cat is subtype of feline" et `at least 51% of wheel part of: a car´ se lit
"at least 51% of wheels are part of a car".
3. `*st subtype of: *t´ (alias, `*st < *t´) est équivalent à
`any *st instance of: *t´,
i.e., ` `*i type: *st´ => `*i type: *t´ ´ (3ème paraphrase,
informelle cette fois :
"*st est sous-type de *t ssi toute instance de *st est aussi instance de *t").
4. `*t > excl{*st1 *st2}´ <=> `*t > *st1 (*st2 exclusion: *st1)´
(informellement :
*st1 et *st2 sont sous-types de *st et ne peuvent avoir ni sous-type commun,
ni instance commune).
5. Si le nœud destination d'une relation est source/destination d'autres relations,
il faut isoler ce nœud destination et ses autres relations avec des parenthèses
(comme
dans l'exemple du paragraphe précédent) pour que
l'interpréteur du langage puisse
savoir que ces autres relations sont
sur le nœud destination et pas le nœud source.
Similairement, dans une notation textuelle, lorsque 2 relations de même source se
suivent, il faut les séparer par un symbôle (en FL, c'est la virgule ; voir
les exemples).
6. Les noms utilisés dans les nœuds relation/source/destination doivent être des
noms communs/propres (jamais d'adjectif, verbe, ...) au singulier et en
minuscules (sauf pour les noms propres s'ils prennent normalement des majuscules).
7. Les relations qui ne sont pas entre types et/ou des individus nommés
(i.e., pas les relations sous-type/instance mais la majorité des relations)
doivent préciser comment les nœuds source et destination sont quantifiés
Exemples de quantificateurs : "a" (i.e., "there exists a"), "any" (i.e., "by definition, each"),
"every" ("by observation, each"), "most" (i.e., "at least 51%"), "at most 20%",
"0..20%", "at most 20", "0..20", "between 2 and 3", "2..3".
Toutefois, si le quantificateur du nœud source est 'any' - i.e., s'il s'agit d'une
définition -
celui-ci peut être omis : c'est le quantificateur par
défaut pour un nœud source.
Pour le nœud destination, 0..* est le quantificateur par défaut. Donc :
`car part: wheel __[any->0..*, 0..*<-any]´
=> (`any car part: 0..* wheel´ <=>
`car part: 0..* wheel´)
8. Si vous hésitez entre 2 relations dont une seule est transitive, choisissez la
transitive.
Sinon, si vous hésitez entre 2 relations, choisissez la plus basique|générique
(et
utilisez des nœuds concept adéquats pour ne pas perdre en
précision).
Notions de langage "plus évolué" et de "bas niveau" :
* Les (langages) assembleurs
(langages de
1ère génération
ou de
2ème génération)
sont impératifs et ils ne sont pas évolués car ils sont
structurellement proches du
langage machine
(binaire; directement exécutable par un microprocesseur).
En effet, un assembleur a généralement 1 mnémonique pour chaque
commande du
jeu d'instruction. Ce sont essentiellement des
opérations arithmétiques/logiques ou
de contrôle, dont le "jump|goto" et l'appel de procédure.
Il est donc très facilement compilable ou
interprétable, i.e.,
traduisible en langage machine.
* Les langages de 3ème génération,
les 1ers dits de "haut niveau" (au sens de évolué),
sont ceux qui
bannissent l'usage du "jump|goto" au profit de structures de contrôle plus
évoluées comme le "while", "for", "switch", ... et l'appel de procédure.
Ce sont donc des langages de programmation impérative (et procédurale) structurée.
* Plus généralement, un langage est plus évolué qu'un autre s'il
offre des
constructions (types d'instructions ou de descriptions) plus abstraites/expressives, i.e., si
grâce à elles, plus d'inférences logiques ou autres opérations
- peuvent être effectuées par un moteur d'inférences ou le
compilateur//interpréteur ; ou
- peuvent être représentées, e.g., concernant les fonctions, le C est +
évolué que Java car
une fonction est une entité du 1er ordre
(i.e., elle peut être mise dans une variable)
en C - via les "pointeurs sur fonctions" - alors que ce n'est pas le cas en Java ; ou
- doivent être opérées par le compilateur//interpréteur pour
traduire ces constructions en assembleur ou langage machine.
Les langages déclaratifs ont donc des constructions plus évoluées que les
langages impératifs. Les langages de
5ème génération sont déclaratifs.
Ceux dits de
"4ème génération" sont le plus souvent impératifs
mais accompagnés d'un
Environnement de programmation/développement intégré
[Integrated Development Environment
(IDE)].
Il y a une équivalence
("correspondance de
Curry-Howard") entre des systèmes de
i) déduction logique,
ii) vérification de types (dans les langages fonctionnels), et
iii) preuve de programme. Toutefois, les langages logiques permettent
d'exprimer
toutes sortes de relations, pas seulement des fonctions.
* Un langage est parfois dit "de bas niveau" si son jeu d'instructions
permet aussi
d'effectuer des opérations sur des bits
(ET, OU, décalage, ..). Le C est donc aussi un
langage de bas niveau
mais n'est pas "moins évolué" que le langage Pascal.
Le C est moins évolué que C++.
Toutefois, l'expression "(plus) haut niveau" [high(er) level] est très
souvent utilisée
dans le même sens que "(plus) évolué".
Exercice :
Un langage évolué de bas niveau ...
A) est un assembleur
B) est moins évolué qu'un langage évolué de haut niveau
C) a des instructions permettant de manipuler les bits, un par un
D) les 3 dernières réponses
E) aucune des 4 dernières réponses
Réponse C
Génie logiciel
[software engineering] :
ingénierie du
développement de logiciel
[software development] ->
l'ensemble des
"activités de conception et de mise en œuvre des produits et des
procédures
tendant à rationaliser la production du logiciel et son suivi"
[arrêté ministériel du 30.12.1983].
But principal: arriver à ce que des logiciels de grande taille
correspondent aux
attentes du client, soient fiables, aient un coût d'entretien réduit
et de bonnes
performances tout en respectant les délais et les
coûts de construction.
Les tâches|processus principaux de
développement|conception de logiciel
[software development process]
sont celles du plan de ce cours de GL1:
1. Modélisation|analyse|spécification & conception
(-> partie 1 du cours)
1.1. Analyse & spécification des besoins
(fonctions que devra offrir le logiciel)
1.2. Conception du logiciel (ses mécanismes) et de ses
bases d'informations
[software design and
knowledge/data base design]
2. Opérationnalisation|réalisation|programmation
[programming]
2.1. Codage|implémentation|construction
[implementation|coding]
dont le
prototypage
[prototyping] et la
gestion de versions
2.2. Intégration,
vérification par analyse ou
test
[software testing],
débogage [debugging]
et optimisation
("validation" et "vérification" sont parfois distinguées mais hélas différemment, e.g., :
- validation : a. via des tests, ou bien
b. revues, prototypage ... -> conformité aux attentes du client
- "vérification" : a. via la lecture de modèles/documentation et de code
b. revues, tests, preuves -> conformité aux spécifications)
3. Déploiement|livraison
[software deployment]
(dont la
documentation pour utilisateurs
, le packaging et le
licensing),
maintenance
[maintenance] et
refactorisation
Exercice :
a. Quelles tâches doivent être documentées ?
b. Donnez, en 1 ligne de script shell, un exemple de prototype pour Xfct1.1.
a. Toutes. b. find . -P -type f -name '*.bmp' -exec wc -c -w -l '{}' \; -P : "Never follow symbolic links" ; "-type f" : select only 'regular' files find offers many options for selecting file on their types, creation/modification/... dates, ... do "man find" to list the options and show+explain different possibilities to the client
Ordonnancement des tâches de développement
[software life cycle] :
lié aux coûts et besoins
de revenir sur une tâche antérieure, compte-tenu
du temps et nombre de personnes impliquées dans chaque étape, et de
la facilité (pour les clients et/ou analystes) de spécifier le produit.
Les grandes familles|méthodologies :
- Modèle en cascade
[waterfall model] :
pas de retour car les tâches sont de
+ en + compliquées et coûteuse par rapport à celles qui les
précèdent,
comme dans le BTP (Bâtiment et Travaux Publics).
- Cycle en V (voir figure page suivante) :
le document ou logiciel créé à chaque
étape est soumis à une revue approfondie avant de passer à
l'étape suivante,
et sert au tests après implémentation.
On ne voit ne voit tourner quelque chose qu'à la fin : c'est
l'effet tunnel.
Cette approche suppose peu de besoins de changements
de choix antérieurs
-> détection tardive d'erreurs d'analyse/conception.
En GL, plus de retours (via des prototypes, ...) sont souvent nécessaires car :
- le client ne connaît (ou ne sait exprimer) ses besoins qu'approximativement
et ceux-ci peuvent évoluer,
- les développeurs ne comprennent pas bien les besoins du client,
sont remplacés ou font de mauvais choix technologiques.
- Cycle en spirale
[spiral model] (pour projets + risqués ou
testant des alternatives) :
à
chaque spire, itération sur les phases
d'analyse, conception, codage et test
-> retours via des versions successives de + en + détaillées d'un produit.
- Cycle semi-itératif
[incremental build model] :
itérations courtes lors du codage,
e.g., les méthodes Agile
(dont Scrum et
l'extreme programming)
et les méthodes
"plus basées sur la création de prototypes" dites
Rapid Application Development
(RAD; cliquez sur ce lien pour une comparaison des méthodes).
- cycle itératif
[iterative model] :
itérations selon le cycle
"planifier->développer->contrôler->ajuster->planifier->..."
Le cycle en V :
Illustration de l'importance d'être précis dans toute communication :
spécification, programmation, création de message d'erreurs, ...
There are two ways of constructing a software design: one way is to make it so simple that there are obviously no deficiencies; the other way is to make it so complicated that there are no obvious deficiencies. (C.A.R. Hoare)
A computer language is not just a way of getting a computer to perform operations but rather that it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute. (The Structure and Interpretation of Computer Programs, H. Abelson, G. Sussman and J. Sussman, 1985.)
Programming (and hence learning to program) IS NOT (just) about coding a program that seem to solve a given problem for the case you have thought about, it IS about creating a SCALABLE program that take into account as many situations as possible and delivers good/helpful error messages for all the different kinds of bad input that the user can enter. Scalable implies - READABLE (clear, concise, consistent in following conventions) - MODULAR (as in "one function per task" and "no more than 30 lines per function" and "no more than 10 lines outside functions"; modularization also helps readability and re-usability). It should be remembered that the initial development of a program is usually a small fraction of the time and cost that is spent on it (later maintenances and extensions is where most of the time is spent, even when good programming languages are used and good programming methodologies are followed).
Le Génie Logiciel (GL) débute vers 1968 avec les
problèmes de la
"crise du logiciel" (à lire),
i.e., les difficultés rencontrées
dès la création des premiers gros logiciels : difficultés de
- réalisation de plannings (-> produits non terminés dans les temps),
- maîtrise des coûts/délais de réalisation et de maintenance
- qualité des logiciels (-> produits peu adaptés/fiables/performants).
Ces gros logiciels étaient rendus possibles par la puissance de
calcul/mémoire des
circuits intégrés.
Leurs problèmes vinrent d'un manque de méthodologie de conception.
Cliquez ici pour des compléments d'information sur les points de la partie 0
de ce cours (à lire).
Cliquez ici pour une liste de "bugs"
ayant eu des conséquences graves ; beaucoup auraient
pu être évités si les outils (langage, compilateur, méthodologie, ...)
utilisés avaient été plus
"stricts" et donc plus "formels".
Répartition moyenne des coûts de production selon Boehm (1975) :
- 45% des efforts pré-maintenance pour tester&corriger une mauvaise
spécification/conception
(64% des erreurs sont des erreurs d'analyse/conception ; 36% d'erreurs de codage)
- coût de correction d'erreurs : 50% coût total de production
(coût 'erreurs de spécification' > coût 'erreurs de conception'
> coût 'erreurs de codage')
- coût exorbitant de gestion de versions multiples
- 40%-80% du coût
d'un logiciel est dans sa maintenance.
Quelques
croyances
erronées sur le développement de logiciels :
* un logiciel peut être construit uniquement par assemblage de composants
logiciels
(non, les composants sont loin d'être suffisamment
adaptables/combinables, complets
et sémantiquement organisés)
* ajouter des personnes à une équipe d'ingénieurs permet de
rattraper le retard
(non, car il faut les former, les temps de
communication sont proportionnels à N*(N-1)
- N étant le nombre de personnes impliquées - et beaucoup de tâches
ne sont pas
partitionables ;
loi de Brooks)
* le travail est terminé une fois que le logiciel fonctionne
(non, même avec une génération semi-automatique du code, une
grosse partie du
travail est dans la maintenance).
Critères|facteurs fonctionnels (de qualité) : conformité par
rapport aux spécifications et
donc aussi par rapport aux besoins des
utilisateurs. Ceci est généralement mesuré via des tests.
Critères structurels :
critères non-fonctionnels
(de qualité). Leur satisfaction provient
essentiellement du respect
de bonnes pratiques architecturales. Les plans pour les
satisfaire sont donc
dans le modèle architectural plutôt que dans le modèle de conception.
Les pages suivantes distinguent :
Exercice :
- la testabilité et la portabilité sont-ils des critères de qualité ?
- si oui, à quel grand critère pensez-vous qu'ils soient reliés ?
- voyez-vous d'autres grandes catégories de critères (structurels ou non) ?
Oui, ce sont des sous-critères de celui de maintenabilité (cf. page suivante).
Premiers critères de "sûreté de fonctionnement"|[dependability] mais pas de "[security]" :
Suite des critères de "sûreté de fonctionnement"|[dependability] mais pas de "[security]" :
Oui puisque la définition ci-dessus du critère de sécurité|[safety] réfère à de la "fiabilité|[reliability]" en ajoutant une contrainte supplémentaire : "pour les erreurs/fautes catastrophiques".
Premiers critères de "sûreté de fonctionnement"|[dependability] et de "[security]" :
Si ces 2 mesures ont des contraintes|définitions similaires (e.g., "mesure par ratio du temps" et "lorsqu'un état de panne n'est pas plus sûr qu'un état de fonctionnement"), celle de "disponibilité" peut s'avérer être une spécialisation de celle de "fiabilité". Toutefois, comme indiqué ci-dessus, les sens/mesures de "disponibilité" et de "fiabilité" sont trop divers (et parfois même antagonistes) pour permettre de classer "disponibilité" comme spécialisation de "fiabilité".
Oui puisque la définition ci-dessus du critère d'accessibilité réfère à de l'accessibilité en ajoutant une contrainte supplémentaire : l'accessibilité au plus grand nombre.
Suite des critères de "sûreté de fonctionnement"|[dependability] et de "[security]" :
Critère de "[security]" mais pas de "[dependability]" :
Exercice :
- la "protection de la vie privée" peut-elle être un critère spécialisant
celui de "confidentialité" ?
La "protection de la vie privée" réfère tout d'abord à une tâche. Une tâche ne peut spécialiser un critère. Si l'on définissait un critère "protection de la vie privée", ce critère pourrait spécialiser celui de "confidentialité" si l'on arrivait à lui donner une définition qui ajoute des contraintes par rapport à celle de "confidentialité".
Exemples d'autres critères structurels de qualité d'un système d'information (S.I.) :
Exercice :
- représentez (graphiquement ou en FL) les liens de spécialisation entre les critères
de qualité
cités dans cette section 0.2.
critère_de_qualité > excl { (critère_fonctionnel_de_qualité > (utilisabilité //"satisfaction" de l'utilisateur (-> inclut "originalité") > comprehensibilité opérationalité //... pour aller plus loin, il faudrait ) // préciser le sens de ces termes (capacité_fonctionnelle > (pertinence > généralité) exactitude //... même remarque ) ) (critère_structurel_de_qualité > excl { (sécurité_au_sens_large > (sûreté_de_fonctionnement //dependability > (sûreté_de_fonctionnement__et_pas__sécurité_au_sens_classique > maintainabilité (fiabilité > safety) ) (sûreté_de_fonctionnement__et__sécurité_au_sens_classique > (disponibilité > accessibilité) (intégrité > authenticité) imputabilité ) ) (sécurité_au_sens_classique > sûreté_de_fonctionnement__et__sécurité_au_sens_classique (sécurité_au_sens_classique__et_pas__sûreté_de_fonctionnement > (confidentialité > vie_privée) anonymat ) ) ) (critère_lié_à_la_performance > efficience-rendement-rentabilité complexité_algorithmique ) } ) };
software_quality_criteria < description_medium, > excl { (software_functional-quality_criteria > (usability > learnability operability) (functional_suitability > functional_appropriateness functional_correctness functionality_compliance ) ) (software_structural-quality_criteria > excl { (software_general-security_criteria > (dependability > (dependability_and_not_security > (maintenability > testability observability stability modifiability (reusability > modularity) ) (reliability > robustness fault_tolerance recoverability (resilience > survivability) safety (^reliability in case of catastrophic failure^) ) ) (security_and_dependability > (availability > accessibility) (integrity > authenticity) accountability ) ) (security > security_and_dependability (security_and_not_dependability > (confidentiality > privacy) unlinkability anonymity ) ) ) (software_general-performance_criteria > performance_efficiency/effectiveness/engineering algorithmic_efficiency ) } ) };
Exemples d'organismes produisant des normes+certifications de qualité de logiciels :
- AFNOR (Association Française de Normalisation -> e.g., norme NF ISO/CEI 9126, 1992)
- ANSI (American National Standard Institute)
- DIN (Deutschen Institut für Norming)
- DOD (Department Of Defense)
- IEEE (Institute of Electrical and Electronic Engineers)
- ISO (International System Organization)
Exemples de modèles pour mesurer/améliorer la qualité de logiciel
(et plus généralement les processus d'un projet ou d'une organisation) :
- le Capability Maturity Model Integration (CMMI) du
Software Engineering Institute (SEI) de Carnegie Mellon University (CMU)
- la méthode COCOMO (COnstructive COst MOdel) qui,
l'année scolaire 2011-2012,
fut introduite dans le cours GL2, de même que la méthododologie de tests,
l'analyse de risques, et
les documents d'analyse&conception dans le cadre
d'une démarche cyclique basée sur des objets, notamment avec UML.
GL1 est plus focalisé sur les "principes génériques"
et "bonnes pratiques" (à
suivre ou utiliser pour effectuer des choix)
de modélisation et de codage.
Bonnes pratiques de programmation
[best coding practices] :
ensemble de règles informelles (-> non contrôlables par un
compilateur/interpréteur)
destinées à améliorer la qualité d'un logiciel et simplifier sa
maintenance ainsi que sa
ré-utilisabilité.
Elles incluent "règles|conventions de style de programmation" et
"principes+patrons de conception".
Comme 40%-80% du coût d'un logiciel est dans sa maintenance, elles sont
très importantes.
Plus un développeur ou une équipe de développement
adopte de telles règles,
mieux c'est.
Il faut les suivre de manière systématique (<-> pas de style incohérent ).
Style de programmation
[programming style] :
ensemble de règles|conventions de codage (et souvent de présentation informelle)
facilitant la compréhensibilité d'un code et l'évitement d'erreurs.
Elles peuvent souvent être vérifiées par un
"vérificateur de style" [Checkstyle tool].
Rien n'est pire que l'absence de style ou l'incohérence dans le suivi de règles.
Les paragraphes suivants pour cette section concernent uniquement la
présentation informelle.
Quote:
A computer language is not just a way of getting a computer to perform operations
but rather that it is a novel formal medium for expressing ideas
about methodology.
Thus, programs must be written for people to read, and only incidentally for
machines to execute.
("The Structure and Interpretation of Computer Programs",
H. Abelson, G. Sussman and J. Sussman, 1985).
Rappels de
règles citées dans le cours de coopération.
1. Plus un langage/protocole/outil permet de représenter et d'exploiter
des objets d'information fins (référables et sélectionnables) et
des méta-données précises sur ces objets
(représentations de connaissances, politiques de contrôle
d'accès/usage, ...),
mieux c'est.
2. Séparer l'information (son contenu) de sa présentation (multimédia) :
pour
- faciliter les changements de présentation par les programmeurs et les utilisateurs,
- faciliter l'exploitation automatique de l'information.
Principaux outils actuels:
- des schémas/modèles/langages pour décrire des structures
(e.g., XML), et
- des schémas/modèles/langages pour décrire des présentations
(e.g., CSS, XSLT).
Lorsqu'utilisés directement, les langages de description de structures de
documents, tels XML,
ces outils ne permettent de représenter qu'une fraction très
faible de la sémantique de l'information et
ils ne permettent pas de la représenter d'une manière qui passe à l'échelle.
En effet, comme ils utilisent une structure d'arbre au lieu d'une structure de
graphe, ils ne permettent
pas - ou n'encouragent pas - une représentation explicite de relations
sémantiques entre objets d'information.
Ceci est détaillé dans la section 1.0.7.
3. Des techniques de personnalisation
doivent être utilisées pour
- proposer différents modèles basés sur des groupes/profils,
- proposer des recommandations basées sur les comportements passés
de l'utilisateur ou
basées sur des préférences d'autres
gens (filtrage collaboratif),
- permettre aux utilisateurs d'adapter l'interface directement.
Corollaire de la règle No 1 (page précédente) :
plus les éléments de l'IHM et du contenu sont séparés,
plus ils sont fins, adressables et précisément représentés (organisés),
plus il est possible de permettre leur personnalisation par chaque utilisateur.
Donc :
- un logiciel doit fournir une liste organisée de paramètres de présentation et
un langage adapté pour la présentation
- la caractéristique la plus importante d'une IHM est que
tous les objets liés à un objet particulier
doivent être facilement accessibles à partir de lui.
-> idéalement, chaque objet "intéressant" doit pouvoir être
sélectionné et
sa sélection par un utilisateur conduire à un
menu déroulant contextuel
(e.g., via un menu pop-up) permettant à l'utilisateur
d'explorer, sélectionner et ajouter/modifier tous ses objets reliés,
e.g.,
ses propriétés de présentation, des objets informationnels et
les actions qui peuvent être faites sur cet objet ou via cet objet
(pour les objets reliés qui n'ont pas trait à la présentation,
les modifications doivent
être sans perte ; cf. la
section 2.6.4 du cours de coopération).
Dans des éditeurs de documents structurés bien conçus, cela est possible
mais
l'ensemble des objets reliés est limité à ceux identifiés par les
schémas de
structure, de présentation et d'interaction/événement associés au
document en
cours d'édition.
Pour un meilleur partage/recherche des connaissances et une
meilleure coopération,
les objets doivent être représentés dans une base de connaissances (BC),
et donc
le menu contextuel associé à un objet sélectionné devrait
permettre l'exploration de
la BC à partir du point d'entrée constitué par cet objet.
Règle générale pour le code (composants logiciels, ...) et ses modèles :
pour représenter/programmer une information, il vaut toujours mieux le faire
- dans le langage utilisé pour les entités formelles,
- le plus formellement possible,
- à défaut, sous forme d'annotations (et, à défaut, sous forme de
commentaires),
- dans tous les cas, en utilisant des termes en anglais plutôt qu'en français
(e.g., pour être compris - et donc être plus ré-utilisable - par
plus de personnes).
Similairement, plus la documentation informelle peut être générée à partir
de
représentations formelles, mieux c'est (pour les développeurs et les utilisateurs).
Commentaires [comments] :
les parties informelles d'un programme (ou
d'une base d'informations) qui ne sont pas associées de manière formelle (i.e., dans
un langage formel) à des entités formelles.
Les annotations sont les parties informelles
associées de manière formelle
(donc exploitables automatiquement), e.g.,
celles utilisées par le compilateur Java.
Les commentaires - et, souvent, les annotations - sont ignorés par un
compilateur/interpréteur de programme (quoique Java utilise certaines annotations
pour certaines vérifications).
Par contre, l'interpréteur d'une base d'information n'ignore que les commentaires :
il stocke les annotations avec les entités auxquelles elles sont associées.
Conformément à la règle générale ci-dessus,
un commentaire ne doit être
utilisé pour spécifier une information que lorsqu'il est impossible de
la spécifier autrement, e.g.,
- via des préconditions, post-conditions et messages d'erreur
- via des identifiants explicites suivant des conventions de nommage
- via des annotations (si le language de programmation le permet).
Un commentaire doit apporter de l'information par rapport au code,
il ne doit donc jamais le paraphraser.
Via des commentaires ou des annotations, chaque entête de module (et,
a fortiori,
de programme) doit au moins indiquer
- son/ses créateurs (-> email, home page),
quand il a été créé et modifié, et pourquoi
e.g., pour un fichier HTML :
<meta name="Author" content="email et/ou home page">
pour un fichier texte :
Author: Philippe Martin (www.phmartin.info)
- comment l'appeler (-> exemples d'appels)
- comment le tester (-> noms des fichiers de tests).
Via des commentaires ou des annotations, un programme doit être séparé en
différentes sections (par exemple comme dans
ce fichier afin faciliter la
distinction ou recherche de ses différentes parties, e.g.,
"Data structures and
associated methods", "Application-dependent classes and functions",
"Other classes and functions".
Conventions de nommage
[naming conventions] :
règles pour la création d'identificateurs (de types, variables, fonctions, ...)
dans un programme et sa documentation.
Il vaut mieux utiliser des identificateurs très explicites (donc très longs)
que d'utiliser des annotations
- c'est une application de la règle générale de la page précédente,
- cela évite aussi au lecteur d'aller chercher l'annotation,
et donc simplifie sa vie et/ou évite des erreurs.
Il faut suivre une méthode de nommage systématique et, si possible,
de manière formelle.
* L'approche orientée objet facilite cela. C'est un de ses apports.
Imitez ce style lorsque vous ne pouvez utiliser l'approche orientée objet, e.g.,
si vous ne pouvez écrire "personX.dateOfBirth_set()",
écrivez
"Person_dateOfBirth_set(personX,dateOfBirthX)"
* Avec des langages de représentation de connaissances, il faut
- utiliser la capitalisation usuelle des mots,
- séparer les mots par des délimiteurs (e.g., '_') plutôt que d'utiliser
le
style "InterCap"|"CamelCase"
car ce dernier engendre une
perte d'information (difficilement récupérable),
- ajouter "type" à la fin des types du second-ordre.
* Avec les langages de programmation, les noms de types, classes et
variables globales doivent commencer par une majuscule.
Seules les noms de constantes doivent être entièrement en majuscules.
* Les identificateurs de fichier étant des identificateurs de constantes dans
certains programmes (e.g., des scripts shell),
ne mettez jamais
d'espace, de caractères accentués
et, plus généralement, de caractères non ASCII)
dans vos noms de fichiers.
Donnez toujours une extension (e.g., ".txt") à vos noms de fichiers,
à part éventuellement aux fichiers directement exécutables.
Indentation:
usage des espaces, e.g., pour faciliter la visualisation des blocs.
Il faut choisir un style et le suivre systématiquement.
Quelques bonnes règles sont :
* Pour des raisons de portabilité :
- ne pas dépasser 78 caractères par ligne
(→ utilisez seulement 2 espaces pour indenter un nouveau bloc)
- ne pas utiliser
de "tabulations physiques" [hard tabs], i.e.
ne pas utiliser la touche "tabulation" sauf si elle génère
des espaces normaux
- utiliser un "éditeur de texte pur (ASCII)" pour écrire les programmes,
jamais un "éditeur de document" général comme par exemple Word.
* Pour des raisons de symétrie
- aligner les parenthèses/crochets/accolades (qui se correspondent) en
colonne/ligne,
sauf s'il s'agit de parenthèses délimitant des
paramètres de functions (appel/définition);
il s'agit du
style Horstmann
(ou style Allman
-- alias style BSD -- si l'accolade ouvrante
est seule sur une ligne, ce qui est à éviter car cela est une
inutile perte d'espace et donc
de travail supplémentaire pour la
visualisation/comparaison/compréhension/ré-utilisation
de fonctions);
- écrire "a==1" ou "a == 1" pas "a ==1" ni "a== 1".
* Pour des raisons d'asymétrie et pour faciliter vos recherches
- écrire "a= 1" pas "a =1" ni "a=1" (sauf pour gagner de la place,
e.g., dans la partie
initialisation d'une boucle "for")
- écrire "int fct (...) {...}" pas "int fct(...) {...}"
et "fct(...);" pas "fct (...);"
- dans une déclaration de fonction, si le langage ne vous le permet pas,
indiquer en commentaire le type des variables et
si oui ou non ce sont des variables d'entrée et/ou de sortie, e.g.,
int fct (/*inP*/..., /*varP*/..., /*outP*/) {...}
* Pour des raisons de concision (et donc de lisibilité) :
- ne pas sauter de ligne dans une fonction/procédure.
* Pour des raisons de lisibilité et de structure (et donc de compréhensibilité,
maintenabilité, ...) :
- utiliser des fonctions (plutôt que des procédures) les + courtes possibles
-> de préférence 2 lignes, jamais plus de 20 lignes
- du code source généré (par un de vos programmes) doit sastifaire les mêmes
conventions et contraintes de lisibilité que vos autres programmes.
Tout tester : paramètres, résultats d'une fonction si la fonction ne les teste pas
elle-même, ...
En début/fin de fonction : test des préconditions et des postconditions
-> intérêt d'avoir des fonctions très courtes qui font elles-même leurs tests.
Donnez des messages d'entrée-sortie (dont les messages d'erreur) "explicites", i.e.,
avec toutes les informations nécessaires permettant à un utilisateur de comprendre
le message
(idéalement, sous la forme d'un "menu déroulant contextuel" permettant de naviguer
la base de connaissances sous-jacente au code pour comprendre les relations entre
les différents objets et fonctions). E.g.,
- un message d'erreur ne doit pas dire "ce que vous avez entré n'est pas valide" mais
"vous venez d'entrer ceci : ... ; cela ne correspond pas au format attendu qui est : ..."
- un résultat (e.g., un nombre) doit toujours être précédé d'une phrase
expliquant
de quoi il s'agit
- une demande d'entrée doit toujours mentionner (ou référer) le format attendu
et
les "valeurs par défaut" qui seront prises en compte.
pm#structure_map := "generic function for any collection traversal/... (-> generalizes most functions); way to create a fully generic and easy-to-search software component library; see also: lselectAndExploit.js (alias, "search & perform") + comments; the equivalent object-oriented alternatives are - to use a Structure_map class, - to group function parameters via a Navig object with various constructor/init methods (with different parameters; flexible memory allocation technique; return Null if error), - more commonly, to give at least 1 for/foreach method to each structure", pm#input: 1 pm#structured_object ?input_structure 0..1 pm#function_with_input_a_structure_and_an_index ?fctToApply/*?s,?i*/, 0..1 pm#boolean_function ?fct_isAtStructureEnd/*?s,?i*/ 0..1 pm#function_returning_a_structured_object ?fct_accessElement/*?s,?i*/ 0..1 pm#function_with_input_a_structure_and_an_index ?fct_inputSuccessor/*?s,?i*/ //E.g., string_left-to-right_next_uppercase_char_from_3rd_char_to_4th'A'() 0..1 pm#index ?index 0..1 pm#function_returning_an_index ?fct_nextIndex/*?i,(?s)*/ 0..1 pm#output_aggregation_function_for_structure_map ?fct_outputAggregation /*?r,?i*/ //sets a successor relation between the results or aggregates them 0..1 pm#index ?outputIndex 0..1 pm#function_returning_an_output_aggregation_type ?outputAggregation_type 0..1 pm#structured_object ?future_output_structure, pm#output: 0..1 pm#structured_object ?output_structure, > (pm#fct_applying_a_function_to_a_structure pm#input: 1 pm#structured_object ?input_structure 1 pm#function_with_input_a_structure_and_an_index ?fctToApply/*?s,?i*/, > excl { (pm#fct_applying_a_function_to_an_indexed_structure = pm#fct_indexed-based_map, pm#input: 1 pm#structured_object ?input_structure, 1 pm#function_with_input_a_structure_and_an_index ?fctToApply 0..1 pm#boolean_function ?fct_isAtStructureEnd 0..1 pm#function_returning_a_structured_object ?fct_accessElement 0..1 pm#index ?index 0..1 pm#function_returning_an_index ?fct_nextIndex 0..1 pm#output_aggregation_function_for_structure_map ?fct_outputAggregation 0..1 pm#index ?outputIndex 0..1 pm#function_returning_an_output_aggregation_type ?outputAggregation_type 0..1 pm#structured_object ?future_output_structure, ) (pm#fct_applying_a_function_to_a_successor-based_structure = pm#fct_successor-based_map, pm#input: 1 pm#structured_object ?input_structure, 0..1 pm#function_with_input_a_structure_and_an_index ?fctToApply 0..1 pm#boolean_function ?fct_isAtStructureEnd 0..1 pm#function_returning_a_structured_object ?fct_accessElement 0..1 pm#function_with_input_a_structure_and_an_index ?fct_inputSuccessor 0..1 pm#output_aggregation_function_for_structure_map ?fct_outputAggregation 0..1 pm#function_returning_an_output_aggregation_type ?outputAggregation_type 0..1 pm#structured_object ?future_output_structure, ) } );
Plus un programme utilise des concepts/constructions abstraites, plus il sera
générique/réutilisable et paramétrable par le programmeur et l'utilisateur.
C'est plus simple avec des langages de haut niveau mais elles peuvent être définies
dans la plupart des langages. Exemple en C++ (explications données oralement) :
class ProcessParameters : public Associator //e.g., an associative array { //inherited methods : set, get, search, print, addToList, ... //e.g.: someProcessParameters.set({"inputMedium",stdin}); // someProcessParameters.set({ {"inputMedium",stdin}, {"outputMedium", stdout}}); public: ProcessParameters (Associator &a) { /* copy 'a' into 'this' */ } ProcessParameters (/*inP*/Medium ¶meterMedium, //"inP": input-only parameter Medium &inputMedium, //no need to write /*inP*/: it is the default Medium &outputMedium, Medium &errorMedium) { this->parameterMedium= parameterMedium; this->inputMedium= inputMedium; this->outputMedium= outputMedium; this->errorMedium= errorMedium; } }; class Process : Thing { public: ProcessParameters *pp; Process () { this->pp= new ProcessParameters(); } Process (ProcessParameters &processParameters) { this->pp= new ProcessParameters(processParameters); } Process (void *fct()) { this->pp= new ProcessParameters({"coreCall()",fct}); } void* run () //run the preCondition, then the coreCall, then the postCondition { if (!core_run("preCondition")) return NULL; void *returnValue= core_run("coreCall"); if (!core_run("postCondition")) return NULL; return returnValue; } void *core_run (const char *fctKind_name, bool isCondition) { void *errorFct()= pp->get("errorFct"); if (!errorFct) errorFct= fprintf; Medium *errorMedium= pp->get("errorMedium"); if (!errorMedium) errorMedium= stderr; void *fct()= pp->get(fctKind_name); if (!fct) return NULL; void *returnValue= (*fct)(pp); //the called functions have pp has unique parameter if (!returnValue && isCondition) (*errorFct)(errorMedium,"%s '%s' not satisfied",fctKind, fct->toString(pp)); return returnValue; } };
class InformationSelectionAndExploitation : Process { InformationSelectionAndExploitation () { ... //as in Process() } void* run () { Process *pr= pp->get("coreCall"); //normally, no coreCall is already set if (!pr) pr= pp->set("coreCall",this->coreCall); //defined below return pr->run(); //Process.run is called, not this one } void* doNothing () { return NULL; } void* coreCall () { Process *selectionLoopInitialization= pp->get("selectionLoopInitialization"); if (!selectionLoopInitialization) selectionLoopInitialization(doNothing()); Process *selectionLoopFilter= pp->get("selectionLoopFilter"); if (!selectionLoopFilter) selectionLoopFilter(doNothing()); Process *selectionLoopStepIfNotAtEnd= pp->get("selectionLoopStepIfNotAtEnd"); if (!selectionLoopStepIfNotAtEnd) selectionLoopStepIfNotAtEnd(doNothing()); Process *selectionLoopAction= pp->get("selectionLoopAction"); if (!selectionLoopAction) selectionLoopAction(doNothing()); for ( selectionLoopInitialization->run(); selectionLoopFilter->run(); selectionLoopStepIfNotAtEnd->run() ) { pp->addToList("results",pp->selectionLoopAction->run()); } return (pp->get("results")); } }; class Xfct1_1 : InformationSelectionAndExploitation { Xfct1_1 () { if (!pp->selectionLoopInitialization()) pp->set("selectionLoopInitialization", directoryExplorationStartingFromCurrentDirectory); if (!pp->selectionLoopStepIfNotAtEnd()) pp->set("selectionLoopStepIfNotAtEnd", depthFirstDirectoryExplorationExceptForSymbolicLinks);; //by default, no filtering : //if (!pp->get("selectionLoopFilter"))) // pp->set("selectionLoopFilter",doNothing()); if (!pp->get("selectionLoopAction"))) pp->set("selectionLoopAction",display_nbBytes_nbWords_nbLines_of_parameter); } };
Toutes les parties de processus/programme, et leur interactions, peuvent se définir en
logique via des ontologies. En voici une adaptée au début de la phase de modélisation ou au
partage de connaissances (raisons et guides donnés en section 1.0, e.g., en section 1.0.7).
process input : 1..* (information //below, within "(. )": any input_medium, not just the 1..* medium: 1..* (. input_medium > (input_file > parameter_file stdin) graphical_interface command_line_parameter ) ), input_medium: 1..* input_medium, //irrelevant here but good for parametering purposes (see next page) output: 1..* (information medium: 1..* (. output_medium > (output_file > stdout stderr) graphical_interface ) ), > (information_selection_and_exploitation part: 1..* (. information_selection_process < process, //selection = exploration + filtering part: 1..* information_exploration_process //e.g., directory traversal with 'find' 1..* information_filtering_process //e.g., file selection with 'find' ) 1..* information_exploitation_process, > Xfct1 //actually, as reminded in the next page, like the command 'find', Xfct1 is an // information_selection_and_exploitation process that embeds other ones, e.g., ); // 'selecting files' is one, 'selecting characters/words/... within a file' is another information_exploration_parameter_description < description, //+ idem for filtering description of: a (. information_exploration_parameter > (info_exploration_starting_point =< collection) //any collection can be used (info_exploration_method < process, > depth_first_exploration breadth_first_exploration ) (info_exploration_depth < integer) (info_exploration_constraint < state //collection of the states in which certain // objects (e.g., directory files) should be if they are to be explored, e.g., // E.g., a state specifying the type, creation/update date and access permission ) // of a file );
Dans l'ontologie précédente, Xfct1 aurait pu être plus détaillée.
Toutefois, si le but est
simplement de réutiliser cette ontologie comme un
fichier de paramètres pour un programme,
- de nombreuses relations dans l'ontologie vont être inutiles, d'autres vont
manquer (e.g., à la
page précédente, "input_medium" est
un raccourci qui permet de gagner quelques lignes de
code dans la paramétrisation d'un programme)"
- des langages moins expressifs (et beaucoup moins adéquats pour la
modélisation et le partage
de connaissances) - en fait, la plupart des langages de description de structure de données -
peuvent être utilisés, e.g., XML ou JSON.
JSON est la notation de
Javascript - et d'un certain nombre d'autres langages - pour décrire
des structures de données. Il est beaucoup plus concis que XML et sera un jour probablement
plus utilisé que XML, au moins en programmation. Sa syntaxe ressemble à celle FL (le langage
que vous utilisez depuis le début de l'année scolaire) mais il est insuffisant pour la
modélisation
et le partage de connaissances.
Exercice (après avoir étudié JSON et lu la section 1.0):
pourquoi est-il insuffisant pour cela ?
Comme connaître JSON vous sera utile (étudiez le !) et qu'il est suffisant pour décrire des
des fichiers de paramètres, la page suivante donne un exemple du contenu d'un tel fichier en
JSON (au lieu de FL) pour un programme qui l'exploite afin d'implémenter de manière flexible
les fonctions principales de Xfct1
(cliquez ici pour l'arcane d'un tel programme).
Plus précisément, l'exemple est en
JSON-LD car c'est la version de JSON
conçue par le
mouvement pour les Linked Data (LD) - i.e., pour le Web Sémantique - et c'est
donc une
version plus précise et expressive que la plupart des autres.
Il serait assez simple de construire, par exemple en "XML (schema)" un "schéma de document"
(Document Data Type) pour les structures utilisables dans un tel fichier de paramètres.
Comme une "ontologie de paramètres de programme" (ontologie pour la phase
d'opérationalisation donc, pas pour la phase de modélisation), ce schéma définirait le
termes (types et valeurs/instances) utilisables dans le programme pour sa paramétrisation.
Toutefois, comme la liste de ces termes peut être aisément déduite de l'exemple en
page suivante, un tel schéma n'est pas ici donné. L'entête du programme peut faire
référence à un fichier de paramètres dont les commentaires permettent de dériver
les différentes possibilités, au lieu de faire référence à un tel schéma/ontologie.
Un but du schéma (ou de l'ontologie) sous-jacent(e) à ce fichier est de pouvoir traiter
tous les processus de "selection et exploitation" de la même manière, e.g.,
- l'exploration et sélection de fichiers dans un répertoire (option prise ci-dessous) ou
de nœuds dans un(e) liste/tableau en mémoire (option en commentaires dans le programme exemple)
- la sélection de caractères, mots, lignes, ... dans un fichier non-répertoire ou une liste.
{ "@type": "process",
"input_medium": "stdin", //warning: "stdin" is a string; a program needs to recognize
//this keyword and replace it with the "standard input" object
"normal_output_medium": "stdout", "error_output_medium": "stderr",
"mainFct":
{"@id": "Xfct1", //for clarity purposes only (the program does not use "@id" attributes)
"@type": "info_selection_and_exploitation", //1st, the selection of files
"info_exploration_content_type": "directory", //or: "within_a_file", "list", ...
"info_exploration_content": "/usr/lib/", //example for "directory" above
//"info_exploration_content": "{a b {c d e {f} g} h i}", //example for "list" above
"info_exploration_method": "depth_first_exploration",
"info_exploration_depth": -1, //no exploration depth limit
"info_exploration_positive_constraint":
{"@type": "file_directory"}, //no other constraint (e.g., on creation dates, ...)
"info_exploration_negative_constraint":
{"@type": "symbolic_link"}, //symbolic links should not be explored/traversed
"info_filtering_positive_constraint": [], //none
"info_filtering_negative_constraint": [ {"@type": "file_directory"} ],
"info_exploitation_process_on_selected_info":
{"@id": "selection_and_exploration_of_each_selected_file",
"@type": "info_selection_and_exploitation",
"info_exploration_content_type": "within_a_file", //binary/textual file
//"info_exploration_content" not used here since the file is selected as above specified
"info_exploration_starting_point": 0, //the beginning
"info_exploration_method": "sequential", //goes to the next character/record at each step
"info_exploration_depth": -1, //if the file is an index, explore the indexed file
"info_exploration_positive_constraint": {}, //all characters/records are considered
"info_exploration_negative_constraint": {}, //all
"info_filtering_positive_constraint": {}, //an index file also belongs to the selection
"info_filtering_negative_constraint": {"@type": "index_file"},
"info_exploitation_process_on_selected_info":
{"@id": "Xfct1_exploitation_of_each_selected_character_or_record"
"input_medium": "menu", //to test inheritance overloading
"positionOfTheWordToDisplay": 10, "wordX": "hello", "maxNbWordXByLine": 12,
"display_of_nbBytes_average": "bold", "display_of_nbWords_average": "invisible",
"info_exploitationAtEnd": "Xfct1::exploitationAtEnd" //useless with a C/C++ program
} // unless the programmer associates the names with a function pointer;
} // in Javascript, this is done by the interpreter
}
}
Exercice de TD noté (1er envoi avant le 31/03/2014 ;
corrections orales le 31/03/2014 et 7/04 ;
envoi final avant lundi 14/04/2014) :
implémentation (seul ou en binôme) d'une solution "générique et
paramétrable"
pour Xfct1 ; pour vous guider,
l'arcane du programme en C++ vous est fournie ;
il vous faut le compléter aux endroits indiqués par les commentaires
"to be implemented" (+ "to be completed" dans l'entête).
D'autres recommandations sont données dans ce programme.
Veuillez utiliser l'anglais pour vos noms de variables et vos commentaires.
Vous pouvez utilisez d'autres languages que C++ mais il vous faudra alors re-écrire
le programme avant de pouvoir le compléter.
1 point sera enlevé pour toute règle (donnée dans ce cours) non respectée.
Toutefois, pour ce programme, la règle des "70 caractères maximum par ligne" est
remplacée par "100 caractères maximum par ligne".
Vous êtes invités à poser des questions (sur le sujet et vos programmes).
Vous devez appliquer les règles dès l'écriture de vos programmes,
pas dans une 2nde phase.
Pour des raisons d'efficacité, votre environnement de travail (disposition de vos
fenêtres, ...)
ne doit pas vous obliger à le modifier
(-> pas de fenêtre l'une sur l'autre, pas de
nécessité d'iconification/ouverture, ... ;
à titre d'exemple, voyez les différences entre
ce poisson et
ce poisson
dans la même fenêtre puis dans deux fenêtres côte à côte ;
-> avantage des fenêtres côte à côte pour la détection de bugs,
la compréhension, ...
-> avantage d'avoir un code concis).
Pour faciliter mes contrôles/aides durant vos TPs, mettez votre éditeur de texte sur la
partie droite de votre écran et les entrées-sorties de votre programme sur la partie gauche.