Introduction

Problématique

La programmation orientée objet s’attache à représenter fidèlement les entités du monde réel. Or, ces entités forment des ensembles, des collections et ne sont pas isolées. Une tâche fondamentale consiste donc à gérer des collections d’objets, et à permettre à un objet d’accéder aux objets auxquels il est lié.

L’objectif de cette activité consiste à travailler :

  • la gestion des collections (tableaux) d’objets : parcours, ajout, recherche,
  • l’accès aux attributs d’un objet,
  • l’accès aux objets associés (navigation).

Ce type d’exercice est caractéristique des sujets d’examens évaluant les compétences de base en programmation orientée objets.

Exemple

L’exemple retenu est celui d’une classe Organisation et d’une classe Stage. Une organisation peut avoir proposé plusieurs stages, mais un stage a eu lieu dans une seule organisation. L’objectif est d’écrire une application permettant :

  • d’afficher la liste des organisations ;
  • d’afficher la liste des stages d’une organisation ;
  • d’afficher la liste des stages, avec pour chacun d’eux l’organisation d’accueil ;
  • d’ajouter un stage ;
  • de rechercher un stage.

Partie 1 : Définition des classes de base

La classe Stage

Un objet de la classe Stage à un trois attributs : l’année de réalisation, le sujet et l’organisation ou il s’est déroulé :

from __future__ import annotations
import organisation

class Stage:
    _annee: int
    _sujet: str
    _organisation : Organisation

    def __init__(self, annee: int, sujet: str, org: Organisation):
        #A COMPLETER

    def getAnnee(self) -> int:
        return self._annee

    def getSujet(self) -> str:
        return self._sujet

    def getOrganisation(self) -> Organisation:
        #A COMPLETER

La classe Organisation

LA classe Organisation représente une entité qui peut proposer plusieurs stages. Elle contient des informations propres à l’organisation (le nom et la ville) ainsi que les stages, tableau d’objets de la classe Stage.

from __future__ import annotations
from typing import List
import stage

class Organisation:
    _nom: str
    _ville: str
    _stages: List[Stage]

    def __init__(self, nom: str, ville: str):
        self._nom = nom
        self._ville = ville
        self._stages = []

    def getNom(self) -> str:
        return self._nom

    def getVille(self) -> str:
        return self._ville

    def getStages(self) -> List[Stage]:
        #A COMPLETER

    def ajoutStage(self, stg: Stage) -> None:
        #A COMPLETER

Le programme principal

Initialisation des données

Le programme principal comprend une fonction initialiser_donnees qui, pour cette version, crée un jeu d’essai :

from __future__ import annotations
from typing import List
from stage import Stage
from organisation import Organisation

def initialiser_donnees(organisations: List[Organisation], stages: List[Stages]) -> None:
    """
    Initialise la liste des organisation et des stages
    @param organisations la liste des organisations, en entrée-sortie
    @param stages la liste des stages, en entrée-sortie
    """
    orga_cps = Organisation("CPS", "Papeete")
    orga_logis = Organisation("Logis", "Faa'a")
    orga_mairie = Organisation("Mairie d'Arue", "Arue")
    orga_opt = Organisation("OPT", "Pirae")
    orga_proxi = Organisation("Prox-i", "Papeete")
    organisations.extend([ orga_cps, orga_logis, orga_mairie, orga_opt, orga_proxi ])

    stage_dev_web = Stage(2024, "Développement d'un site web", orga_cps)
    orga_cps.ajoutStage(stage_dev_web)
    stage_api_python = Stage(2024, "Création d'une API en Python", orga_logis)
    stage_maintenance = Stage(2023, "Maintenance d'une application Java", orga_logis)
    orga_logis.ajoutStage(stage_api_python)
    orga_logis.ajoutStage(stage_maintenance)
    stage_intranet = Stage(2024, "Refonte de l'intranet", orga_mairie)
    orga_mairie.ajoutStage(stage_intranet)
    stage_data = Stage(2024, "Analyse de données client", orga_opt)
    orga_opt.ajoutStage(stage_data)
    stage_workflow = Stage(2025, "Gestion des flux de travail", orga_proxi)
    stage_lowcode = Stage(2024, "Création d'une application Low-Code", orga_proxi)
    orga_proxi.ajoutStage(stage_workflow)
    orga_proxi.ajoutStage(stage_lowcode)
    stages.extend([ stage_api_python, stage_data, stage_dev_web, stage_intranet, stage_maintenance, stage_workflow, stage_lowcode ])

Travail à faire : compléter le jeu d’essai en y ajoutant deux autres stages de votre convenance.

Approfondissement :

  • Insérer les stages et organisations dans une base de données (SQLite par exemple) ; remarque : une IA peut aisément transformer le code Pythob en instructions SQL.
  • Charger les données depuis la base de données ; cf documentation Python/SQLite.

Le menu

L’application, en mode console, comprend la fonction utilitaire menu permettant à l’utilisateur de sélectionner l’opération à réaliser :

def menu(choix : List[str]) -> int:
    print("")
    for i in range(len(choix)) :
        print(i,":",choix[i])
    reponse = int (input("votre choix : "))
    print("")
    return reponse

L’affichage de la liste des organisations

Objectif pédagogique: parcours de collection.

Cette fonction a pour rôle de présenter à l’utilisateur la liste des organisations disponibles. Elle reçoit en paramètre un tableau d’objets Organisation et doit parcourir cette collection pour afficher les informations essentielles de chaque organisation (son nom et sa ville). L’objectif est de fournir un affichage clair et numéroté pour que l’utilisateur puisse ensuite sélectionner une organisation par son numéro.

Exemple d’affichage attendu :

Liste des organisations :
1 : CPS, Papeete
2 : Logis, Faa'a
3 : Mairie d'Arue, Arue
4 : OPT, Pirae
5 : Prox-i, Papeete

Profil et spécification de la fonction :

def afficher_organisations(organisations: List[Organisation]):
    """
    Affiche la liste des organisations.
    @param organisations la liste des organisations
    """
    #A COMPLETER: pour chaque organisation, afficher son nom et sa ville

L’affichage de la liste des stages

Objectif pédagogique : navigation d’un objet à un autre objet associé.

Cette fonction reçoit une liste de stages et doit afficher, pour chaque stage, son année, son sujet, ainsi que le nom de l’organisation qui l’accueille. Pour obtenir ce dernier élément, il est nécessaire de “naviguer” de l’objet de la classe Stage vers l’objet Organisation auquel il est lié.

Concrètement, pour chaque stage de la liste, il faut :

  • utiliser l’accesseur getOrganisation() pour obtenir l’objet de la classe Organisation correspondant au lieu du stage ;
  • une fois cette organisation obtenue, il faut utiliser sa méthode getNom().
def afficher_stages(stages: List[Stage]):
    """
    Affiche la liste des stages avec le nom de l'organisation.
    @param stages la liste des stages à afficher
    """
    #A COMPLETER: pour chaque stage, afficher son année, son sujet
                                    #ainsi que le nom de l'organisation

L’ajout d’un stage

Cette fonction permet d’enrichir le jeu de données en créant une nouvelle instance de Stage. Elle reçoit en paramètres toutes les informations nécessaires à cette opération (la liste des stages, l’organisation, l’année et le sujet). Le travail consiste à :

  1. Créer une nouvelle instance de la classe Stage à partir des paramètres.
  2. Ajouter ce stage aux stages de l’organisation concernée (cf ajoutStage())
  3. Ajouter ce nouvel objet à la liste des stages.
def ajouter_stage(stages: List[Stage], org: Organisation, annee: int, sujet: str):
    """
    Instancie et ajoute un stage à une organisation et à la liste des stages.
    @param stages la liste des stages
    @param org l'organisation du stage
    @param annee l'année du stage
    @param sujet le sujet du stage
    """
    #A COMPLETER: instancier un nouveau stage,
                 #l'ajouter à l'organisation et à la liste des stages.

Remarque : au niveau de la conception, on aurait pu imaginer, que c’est le constructeur de la classe Stage qui appelle lui même la méthode ajoutStage : org.ajoutStage(self).

La recherche de stage

Objectif pédagogique : réviser la recherche séquentielle.

L’objectif de cette fonction est de retourner une nouvelle liste ne contenant que les stages qui correspondent au terme de recherche passé en paramètre (soit l’année, soit un partie de texte du sujet). La stratégie est la suivante :

Pour chaque stage, l’ajouter à la sélection s’il correspond au terme de recherche : - le terme correspond à l’année du stage (attention aux types) ; - ou le terme recherché apparaît dans le sujet du stage.

def rechercher_stage(stages: List[Stage], terme: str) -> List[Stage]:
    """
    Recherche des stages par année ou par sujet.
    @param stages la liste des stages à filtrer
    @param terme le terme de recherche (année ou partie du sujet)
    @return la liste des stages correspondants au terme de recherche
    """
    selection: List[Stage] = []
    #A COMPLETER : Implémenter la stratégie de recherche décrite ci-dessus
    return selection

Le point d’entrée

Voici le point d’entrée du programme, à compléter :

if __name__ == "__main__":
    organisations: List[Organisation] = []
    stages: List[Stage] = []
    initialiser_donnees(organisations, stages)
    choix = -1
    while choix != 0:
        choix = menu(["Quitter", "Liste des organisations", "Stages d'une organisation", "Liste des stages", "Ajouter un stage", "Rechercher un stage"])
        if choix == 1:
            afficher_organisations(organisations)
        elif choix == 2:
            afficher_organisations(organisations)
            num = int (input("n° de l'organisation : "))
            if num > 0 and num <= len(organisations):
                #A COMPLETER
        elif choix == 3:
            afficher_stages(stages)
        elif choix == 4:
            afficher_organisations(organisations)
            num = int (input("n° de l'organisation : "))
            if num > 0 and num <= len(organisations):
                annee = int(input("Année du stage : "))
                sujet = input("Sujet du stage : ")
                #A COMPLETER
        elif choix == 5:
            terme = input("Terme recherché : ")
            #A COMPLETER