GL1 - Partie 0

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

0.0. Cas d'utilisation "UseCaseX"

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).

0.0. Cas d'utilisation "UseCaseX" - Exercices

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) :

  1. (1ère version -> vendredi 21/2/2014, 10:00)
    Compte-tenu des précisions obtenues à la question précédente
      (précisions données par l'enseignant) et
    compte-tenu de tout ce qui est écrit ci-dessus, écrivez - dans un langage
      informatique de votre choix (que vous préciserez) -
    votre meilleure fonction (ou procédure) pour calculer nbBytes ;
      "meilleure" au sens de "la plus ré-utilisable".
    Si vous pensez satisfaire d'autres critères, vous devez les citer.
    Ré-utilisable -> générique -> le paramètre principal de la fonction
      nbBytes est de type "Thing" (ou "Chose") et appelle d'autre fonctions
      telles que string_nbBytes ou file_nbBytes.
  2. (1ère version -> vendredi 21/2/2014, 10:00)
    Écrivez - en C++, Java, Javascript ou un autre language impératif - une
    fonction qui calcule et renvoie le nombre d'occurrences de la chaîne de
    caractère "LL" dans une chaîne de caractères passée en paramètre
    de cette fonction. Vous ne devez pas faire appel à une autre fonction.
    S'il y a une ou plusieurs ambiguïtés dans une spécification et que
    vous ne pouvez pas demander au spécifieur de les lever (c'est le
    cas ici pour cette fonction s'il y a une ou plusieurs ambiguïté),
    vous devez  1) le signaler dans votre fonction,
    2) prendre toutes les possibilités en compte en ajoutant à votre fonction
    un ou plusieurs paramètres permettant de lever les ambiguités.
  3. (1ère version -> jeudi 6/3/2014, 10:00)
    Écrivez dans un language impératif ou fonctionnel une fonction
    qui calcule et renvoie le nombre d'occurrences de la lettre 'L' dans une
    liste chaînée dont chaque nœud contient un caractère.
    Contrainte supplémentaire : vous devez parcourir cette liste en partant de la fin et
    en "sautant un nœud sur deux" (attention, ambiguité).

0.0. Cas d'utilisation "UseCaseX" - Exercices

Pour jeudi 27/2/2014, 10:00 :

  1. Essayez de comprendre la notion de "container" et donc, notamment, lisez
    cette page sur les containerscette page sur la bibliothèque standard C++
    et des pages reliées à cette dernière : au moins Sequence container (C++),
    Associative containers et Iterator. Lisez aussi quelques exemples (de code)
    de fonctions pointées par ces pages, e.g., std::vector::vector.
    Vous y trouverez quelques exemples d'utilisation de templates C++.
    Pas d'affolement si vous ne comprenez pas tout.
  2. Après cela, écrivez en C ou autre language non orienté objet (-> pas en
    C++, Java, ...), puis e-mailer à l'enseignant avant jeudi 27/2/2014 10:00
    un programme qui implémente, teste et documente les spécifications suivantes
    - écrivez cette fonction calcule et renvoie le nombre d'occurrence d'éléments
      dans un "container" après avoir navigué sur chacun de ses éléments
      (par exemple comme dans une liste chaînée) ;
    - appelez cette fonction (directement ou pas) pour lui faire calculer le
      nombre de caractères dans une chaîne de caractères ;
    - ne cherchez pas trop loin, c'est un exercice court qui a simplement pour
      but de de vous entraîner à cacher (encapsuler) les types de données
      que vous utilisez pour implémenter une fonction ; voir aussi la note page suivante ;
    - dorénavant, les règles de la section 0.3.2.1 doivent toutes être suivies.

0.0. Cas d'utilisation "UseCaseX" - Exercices

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;
}

Container someCharContainer("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);

0.1. Définitions générales

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 scienceInformation 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).

0.1. Définitions générales

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.

0.1. Définitions générales

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)

0.1. Définitions générales

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
- LISPStandard MLHaskellPrologForth  et
  d'autres langages de programmation plus "ésotériques".

0.1. Définitions générales

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 )
         )
    );

0.1. Définitions générales - Spécifications


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).

0.1. Définitions générales - Langages

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

0.1. Définitions générales - Génie logiciel

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

0.1. Définitions générales - Génie logiciel

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->..."

0.1. Définitions générales - Génie logiciel

Le cycle en V :

0.1. Définitions générales - Communication / Génie logiciel

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).

0.1. Définitions générales - Génie logiciel

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).

0.2. Critères fonctionnels/structurels de qualité logicielle

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.

0.2. Critères fonctionnels/structurels de qualité logicielle

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).

0.2. Critères de "sécurité logicielle au sens large"

Premiers critères de "sûreté de fonctionnement"|[dependability] mais pas de "[security]" :

0.2. Critères de "sécurité logicielle au sens large"

Suite des critères de "sûreté de fonctionnement"|[dependability] mais pas de "[security]" :

0.2. Critères de "sécurité logicielle au sens large"

Premiers critères de "sûreté de fonctionnement"|[dependability]  et  de "[security]" :

0.2. Critères de "sécurité logicielle au sens large"

Suite des critères de "sûreté de fonctionnement"|[dependability]  et  de "[security]" :

0.2. Critères de "sécurité logicielle au sens large"

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é".

0.2. Autres critères structurels de qualité logicielle

Exemples d'autres critères structurels de qualité d'un système d'information (S.I.) :

0.2. Critères fonctionnels/structurels de qualité logicielle

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
             )
           }
      )
    };

0.2. Critères fonctionnels/structurels de qualité logicielle

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
             )
           }
      )
    };

0.2. Critères de "sécurité logicielle au sens large"


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.

0.3. Principes pour la présentation informelle du code et de l'IHM

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).

0.3.1. Principes pour l'IHM

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.

0.3.1. Principes pour l'IHM

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.

0.3.2.1. Principes pour le code - Règle générale ; commentaires

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".

0.3.2.1. Principes pour la présentation du code - Conventions de nommage

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.

0.3.2.1. Principes pour la présentation du code - Indentation

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.

0.3.2.1 Principes pour le code - Tests et messages d'entrée-sorties

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.

0.3.2.2. ... code - Approches plus génériques et paramétrables

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, ) } );

0.3.2.2. ... code - Approches plus génériques et paramétrables

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 &parameterMedium, //"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; } };

0.3.2.2. ... code - Approches plus génériques et paramétrables

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); } };

0.3.2.2. ... code - Approches plus génériques et paramétrables

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 );

0.3.2.2. ... code - Approches plus génériques et paramétrables

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.

0.3.2.2. ... code - Approches plus génériques et paramétrables

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
     }
}

0.3.2.2. ... code - Approches plus génériques et paramétrables

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.