Aller au contenu

Exemples concrets de modélisation UML et Java

Il est indispensable de bien comprendre les conséquences de vos modélisations sur votre code. Ici, nous allons utiliser le langage Java pour illustrer ce que peuvent générer les différentes relations entre les classes de votre diagramme UML.

Il s’agit d’une relation one-to-many (1..* pour Département et 0..1 pour Pays), où un Pays peut avoir plusieurs Département, et chaque Département appartient à un seul Pays.

Classe Departement :

public class Departement {
    private String nom;
    private int numeroDepartement;
    private Pays pays;

    public Departement(String nom, int numeroDepartement) {
        this.nom = nom;
        this.numeroDepartement = numeroDepartement;
    }

    // Getters et setters
    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public int getNumeroDepartement() {
        return numeroDepartement;
    }

    public void setNumeroDepartement(int numeroDepartement) {
        this.numeroDepartement = numeroDepartement;
    }

    public Pays getPays() {
        return pays;
    }

    public void setPays(Pays pays) {
        this.pays = pays;
    }
}

Classe Pays :

import java.util.ArrayList;
import java.util.List;

public class Pays {
    private String nom;
    private int population;
    private List<Departement> departements;

    public Pays(String nom, int population) {
        this.nom = nom;
        this.population = population;
        this.departements = new ArrayList<>();
    }

    // Ajouter un département
    public void ajouterDepartement(Departement departement) {
        if (!departements.contains(departement)) {
            departements.add(departement);
            departement.setPays(this);
        }
    }

    // Retirer un département
    public void retirerDepartement(Departement departement) {
        if (departements.contains(departement)) {
            departements.remove(departement);
            departement.setPays(null);
        }
    }

    // Getters et setters
    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    public List<Departement> getDepartements() {
        return departements;
    }

    public void setDepartements(List<Departement> departements) {
        this.departements = departements;
    }
}

Exemple d’utilisation :

public class Main {
    public static void main(String[] args) {
        Pays france = new Pays("France", 67000000);

        Departement paris = new Departement("Paris", 75);
        Departement lyon = new Departement("Rhône", 69);

        france.ajouterDepartement(paris);
        france.ajouterDepartement(lyon);

        System.out.println("Pays : " + france.getNom());
        System.out.println("Départements : ");
        for (Departement departement : france.getDepartements()) {
            System.out.println("- " + departement.getNom() +
                               " (n°" + departement.getNumeroDepartement() + ")");
        }
    }
}

Explications :

Cardinalités :

Il s’agit d’une association qualifiée où un Répertoire peut contenir plusieurs Fichier, et chaque Fichier est identifié de manière unique dans un Répertoire par son attribut nom.

Classe Fichier :*

public class Fichier {
    private String nom;
    private int taille;
    private String extension;

    public Fichier(String nom, int taille, String extension) {
        this.nom = nom;
        this.taille = taille;
        this.extension = extension;
    }

    // Getters et setters
    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public int getTaille() {
        return taille;
    }

    public void setTaille(int taille) {
        this.taille = taille;
    }

    public String getExtension() {
        return extension;
    }

    public void setExtension(String extension) {
        this.extension = extension;
    }
}

Classe Répertoire :

import java.util.HashMap;
import java.util.Map;

public class Repertoire {
    private Map<String, Fichier> fichiers;  // Association qualifiée : la clé est le nom du fichier

    public Repertoire() {
        this.fichiers = new HashMap<>();
    }

    // Ajouter un fichier au répertoire
    public void ajouterFichier(Fichier fichier) {
        if (fichier != null) {
            fichiers.put(fichier.getNom(), fichier);
        }
    }

    // Retirer un fichier du répertoire
    public void retirerFichier(String nomFichier) {
        fichiers.remove(nomFichier);
    }

    // Obtenir un fichier par son nom
    public Fichier getFichier(String nomFichier) {
        return fichiers.get(nomFichier);
    }

    // Getter pour obtenir tous les fichiers
    public Map<String, Fichier> getFichiers() {
        return fichiers;
    }
}

Exemple d’utilisation :

public class Main {
    public static void main(String[] args) {
        Repertoire monRepertoire = new Repertoire();

        Fichier fichier1 = new Fichier("document", 1024, "txt");
        Fichier fichier2 = new Fichier("image", 2048, "png");

        monRepertoire.ajouterFichier(fichier1);
        monRepertoire.ajouterFichier(fichier2);

        // accès à un fichier par son nom (qualificatif)
        Fichier fichierRecupere = monRepertoire.getFichier("document");
        if (fichierRecupere != null) {
            System.out.println("Fichier trouvé : " + fichierRecupere.getNom() +
                               ", Taille : " + fichierRecupere.getTaille() +
                               ", Extension : " + fichierRecupere.getExtension());
        }
        monRepertoire.retirerFichier("image");
    }
}

Association qualifiée : La relation est implémentée avec une Map<String, Fichier>, où la clé est le nom du fichier. Cela permet d’accéder rapidement à un fichier spécifique dans le répertoire.

Cardinalités :

Encapsulation : Les fichiers sont gérés de manière centralisée dans le répertoire, et chaque fichier est identifié de manière unique par son nom.

Dans une composition, le Moteur ne peut pas exister sans la Voiture. Cela signifie que la durée de vie du Moteur est liée à celle de la Voiture. Quand la Voiture est détruite, le Moteur l’est aussi.

On peut avoir 2 codes différents pour illustrer cette relation.

Solution 1 :

Une classe Moteur publique :

public class Moteur {
    private int puissance;

    public Moteur(int puissance) {
        this.puissance = puissance;
    }

    // Getter et setter
    public int getPuissance() {
        return puissance;
    }

    public void setPuissance(int puissance) {
        this.puissance = puissance;
    }
}

Une classe Voiture contenant un attribut Moteur instancié dans le constructeur de Voiture :

public class Voiture {
    private int immatriculation;
    private String modele;
    private Moteur moteur;  // Composition : le moteur est une partie intégrante de la voiture

    public Voiture(int immatriculation, String modele, int puissanceMoteur) {
        this.immatriculation = immatriculation;
        this.modele = modele;
        this.moteur = new Moteur(puissanceMoteur);  // Création du moteur dans le constructeur de Voiture
    }

    // Getters et setters
    public int getImmatriculation() {
        return immatriculation;
    }

    public void setImmatriculation(int immatriculation) {
        this.immatriculation = immatriculation;
    }

    public String getModele() {
        return modele;
    }

    public void setModele(String modele) {
        this.modele = modele;
    }

    public Moteur getMoteur() {
        return moteur;
    }
    // Mais, pas de setter pour le moteur, car il est créé avec la voiture et ne peut pas être remplacé !
}

Exemple de classe qui utilise Voiture et Moteur :

public class Main {
    public static void main(String[] args) {
        Voiture maVoiture = new Voiture(12345, "Renault Clio", 150);
        System.out.println("Voiture : " + maVoiture.getModele() +
                           ", Immatriculation : " + maVoiture.getImmatriculation() +
                           ", Puissance du moteur : " + maVoiture.getMoteur().getPuissance() + " chevaux.");
    }
}

Solution 2 :

Cette fois-ci le Moteur est une classe privée interne de Voiture. Cela renforce encore plus l’encapsulation et la notion de composition, car le Moteur ne peut exister qu’à l’intérieur de Voiture et n’est pas accessible depuis l’extérieur !

public class Voiture {
    private int immatriculation;
    private String modele;
    private Moteur moteur;  // avec une classe interne privée

    public Voiture(int immatriculation, String modele, int puissanceMoteur) {
        this.immatriculation = immatriculation;
        this.modele = modele;
        this.moteur = new Moteur(puissanceMoteur);  // instanciation du moteur interne
    }

    public int getImmatriculation() {
        return immatriculation;
    }

    public void setImmatriculation(int immatriculation) {
        this.immatriculation = immatriculation;
    }

    public String getModele() {
        return modele;
    }

    public void setModele(String modele) {
        this.modele = modele;
    }

    // méthode pour obtenir la puissance du moteur (sans exposer directement le moteur)
    public int getPuissanceMoteur() {
        return moteur.getPuissance();
    }

    // Notre classe interne privée Moteur
    private class Moteur {
        private int puissance;

        public Moteur(int puissance) {
            this.puissance = puissance;
        }

        public int getPuissance() {
            return puissance;
        }

        public void setPuissance(int puissance) {
            this.puissance = puissance;
        }
    }
}

Exemple d’utilisation :

public class Main {
    public static void main(String[] args) {
        Voiture maVoiture = new Voiture(12345, "Renault Clio", 150);
        System.out.println("Voiture : " + maVoiture.getModele() +
                           ", Immatriculation : " + maVoiture.getImmatriculation() +
                           ", Puissance du moteur : " + maVoiture.getPuissanceMoteur() + " chevaux.");
    }
}

Cette approche est idéale si vous souhaitez cacher complètement les détails du moteur et ne pas permettre sa manipulation directe depuis l’extérieur de la classe Voiture.

Exemple complet du code Java correspondant sachant que notre relation est bidirectionnelle :

Un Travailleur peut être employé (Emploi) dans plusieurs Sociétés. Une Société peut avoir plusieurs Travailleurs (Emplois).

Emploi est une classe d’association qui permet d’associer 2 objets Travailleur et Société en indiquant un :

Classe Emploi :

public class Emploi {
    private String intitule;
    private float salaire;
    private Travailleur travailleur;
    private Societe employeur;

    public Emploi(String intitule, float salaire) {
        this.intitule = intitule;
        this.salaire = salaire;
    }

    // Getters et setters
    public String getIntitule() {
        return intitule;
    }

    public void setIntitule(String intitule) {
        this.intitule = intitule;
    }

    public float getSalaire() {
        return salaire;
    }

    public void setSalaire(float salaire) {
        this.salaire = salaire;
    }

    public Travailleur getTravailleur() {
        return travailleur;
    }

    public void setTravailleur(Travailleur travailleur) {
        this.travailleur = travailleur;
    }

    public Societe getEmployeur() {
        return employeur;
    }

    public void setEmployeur(Societe employeur) {
        this.employeur = employeur;
    }
}

Classe Travailleur :

import java.util.ArrayList;
import java.util.List;

public class Travailleur {
    private String nom;
    private String prenom;
    private List<Emploi> emplois;

    public Travailleur(String nom, String prenom) {
        this.nom = nom;
        this.prenom = prenom;
        this.emplois = new ArrayList<>(); // on peut mettre un liste d'Emploi au Travailleur
    }

    // Ajouter un emploi pour ce travailleur
    public void ajouterEmploi(Emploi emploi) {
        if (!emplois.contains(emploi)) {
            emplois.add(emploi);
            emploi.setTravailleur(this);
        }
    }

    // Getters et setters
    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public String getPrenom() {
        return prenom;
    }

    public void setPrenom(String prenom) {
        this.prenom = prenom;
    }

    public List<Emploi> getEmplois() {
        return emplois;
    }

    public void setEmplois(List<Emploi> emplois) {
        this.emplois = emplois;
    }
}

Classe Employeur :

import java.util.ArrayList;
import java.util.List;

public class Societe {
    private String raisonSociale;
    private List<Emploi> emplois; // on peut mettre un liste d'Emploi à la société

    public Societe(String raisonSociale) {
        this.raisonSociale = raisonSociale;
        this.emplois = new ArrayList<>();
    }

    // ajouter un emploi pour cette société
    public void ajouterEmploi(Emploi emploi) {
        if (!emplois.contains(emploi)) {
            emplois.add(emploi);
            emploi.setEmployeur(this);
        }
    }

    // Getters et setters
    public String getRaisonSociale() {
        return raisonSociale;
    }

    public void setRaisonSociale(String raisonSociale) {
        this.raisonSociale = raisonSociale;
    }

    public List<Emploi> getEmplois() {
        return emplois;
    }

    public void setEmplois(List<Emploi> emplois) {
        this.emplois = emplois;
    }
}