Aller au contenu

Semaine 2 : Jour 3 - Héritages, Interfaces, Classes Abstraites, Polymorphisme et Collections avancées

héritage et humour

Source : lessoir.be

Formation Java / Spring Boot pour développeurs COBOL


Objectifs pédagogiques

À l’issue de cette journée, vous serez capable de :


Mise en contexte (transition COBOL → Java)

En COBOL, il n’existe pas de mécanisme natif pour dire “Tout ce qui se prétend être un traitement de virement doit obligatoirement respecter ces règles” (enfin, je ne crois pas !)

On utilise alors :

Java introduit des interfaces, de l’Héritage et des classes abstraites pour rendre ces règles obligatoires et vérifiables par le compilateur.


Première approche

J’ai conçu le cours de la manière suivante :

Java Partie 1 - sans référence à Cobol

Dans ce cours, nous allons voir les notions importantes en POO à l’aide de diagramme de classe pour bien comprendre les relations d’héritage et l’implication qu niveau du code java, les mots-clés this et super. L’utilisation de instanceof. La différence entre agrégation, composition et héritage.

Lien vers le cours Java sur l’héritage, polymorphisme,…

Lien vers les notions de Modificateur final, Classes abstraites, interfaces (Comparable, TreeSet)

Lien vers une explication simple de l’interface

Quelques TP

Java partie 2 - avec référence à Cobol

Héritage et polymorphisme

Vous avez déjà vu :

public class Compte {
    protected BigDecimal solde;
}

Les classes qui héritent de Compte :

public class CompteCourant extends Compte { }
public class CompteEpargne extends Compte { }

Et l’idée clé :

Compte c = new CompteCourant(...);

Dans ce code on va manipuler un objet du type parent, pas le type réel CompteCourant, c’est le polymorphisme !

Aujourd’hui, on va aller plus loin et plus proprement même si on l’a déjà vu dans la première partie.


Classe abstraite : un concept intermédiaire clé

Une classe abstraite :


Analogie COBOL

Une classe abstraite est comparable à :

un COPYBOOK métier qui définit des règles communes, mais qui n’est jamais exécuté seul.


Compte bancaire abstrait

Pourquoi faire cela selon vous ?

On ne veut plus instancier un Compte sans que cela corresponde à quelque chose de concret !

Compte compte101 = new Compte(...)

Un compte doit toujours être soit de type :


Classe abstraite Compte

Tranformons notre classe concrête Compte en classe abstraite avec le mot-clé abstract. Pour des raisons pédagogiques on ne va pas se préoccuper des vérifications et des exceptions.

public abstract class Compte {

    protected String numero;
    protected BigDecimal solde;

    public Compte(String numero, BigDecimal soldeInitial) {
        this.numero = numero;
        this.solde = soldeInitial;
    }

    public void crediter(BigDecimal montant) {
        solde = solde.add(montant);
    }
    // méthode abstraite
    public abstract void debiter(BigDecimal montant);

    public BigDecimal getSolde() {
        return solde;
    }
}

Points importants :


Implémentations concrètes

Le CompteCourant hérite bien de la classe abstraites Compte et doit définir le code de la méthode debiter()

public class CompteCourant extends Compte {

    private BigDecimal decouvertAutorise;

    public CompteCourant(String numero, BigDecimal solde, BigDecimal decouvertAutorise) {
        super(numero, solde);
        this.decouvertAutorise = decouvertAutorise;
    }

    @Override
    public void debiter(BigDecimal montant) {
        BigDecimal futurSolde = solde.subtract(montant);
        if (futurSolde.compareTo(decouvertAutorise.negate()) < 0) {
            throw new IllegalStateException("Découvert dépassé");
        }
        solde = futurSolde;
    }
}

Le CompteEpargne hérite bien de la classe abstraites Compte et doit définir le code de la méthode debiter()

public class CompteEpargne extends Compte {

    public CompteEpargne(String numero, BigDecimal solde) {
        super(numero, solde);
    }

    @Override
    public void debiter(BigDecimal montant) {
        if (solde.compareTo(montant) < 0) {
            throw new IllegalStateException("Solde insuffisant");
        }
        solde = solde.subtract(montant);
    }
}

Interface : Une question de contrat

Une interface :

En réalité, il peut y avoir des spécificités que nous n’aborderons pas ici.


Analogie avec COBOL

Une interface est comparable à une spécification fonctionnelle ou un contrat d’interface inter-applicative que plusieurs programmes doivent respecter.


Exemple : interface DebitAutorise

public interface DebitAutorise {

    // une seule méthode dans cette interface
    void debiter(BigDecimal montant);
}

Toute classe qui implémente cette interface s’engage à intégrer la méthode debiter (on parle d’implémenter), c’est-à dire de rédiger le code fonctionnel correspondant.


Implémentation

On utilise tout simplement le mot-clé implements pour dire à chacune des 2 classe qu’elles ont un contrat avec l’interface DebitAutorise.

public class CompteCourant extends Compte implements DebitAutorise { }
public class CompteEpargne extends Compte implements DebitAutorise { }

Classe abstraite & Interface

A ne pas confondre.

Classe abstraite Interface
Peut contenir des attributs Pas d’attribut métier
Peut avoir du code Pas de code métier
Héritage simple Implémentations multiples
Modèle “est un” Modèle “peut faire”

Collections avancées

À partir de maintenant, on ne manipule plus UN compte, mais DES comptes, DES clients et DES opérations.

Les collections deviennent centrales, voire indispensables.


List : ordre + doublons autorisés

Utiles pour :

List<Compte> comptes = new ArrayList<>();

Le parcours polymorphique en supposant que nous avons plusieurs sous-types de Compte (Courant, Titre, Epargne et autres).

for (Compte c : comptes) {
    c.crediter(new BigDecimal("100"));
}

Selon vous, quelle est la méthode qui sera appelée dans la boucle for ? Celle de Compte ? de CompteCourant ? de CompteEpargne ?…


Set : unicité garantie

Cette Collection permet d’éviter deux comptes avec le même numéro !

Set<String> numerosCompte = new HashSet<>();
numerosCompte.add("FR001");
numerosCompte.add("FR001"); // ignoré

Correspondance COBOL : contrôle d’unicité manuel et souvent tardif.


Map : clé & valeur

Cette collection facilite l’accès direct à un compte par son numéro.

Map<String, Compte> comptesParNumero = new HashMap<>();
comptesParNumero.put("FR001", compte1);
Compte c = comptesParNumero.get("FR001");

Démonstration guidée complète (objet/interface/collections)

Exemple :

List<Compte> comptes = new ArrayList<>();

comptes.add(new CompteCourant("FR001", new BigDecimal("1000"), new BigDecimal("500")));
comptes.add(new CompteEpargne("FR002", new BigDecimal("800")));

for (Compte c : comptes) {
    c.debiter(new BigDecimal("100"));
    System.out.println(c.getSolde());
}

Travaux pratiques

TP 1 – Refactorisation vers classe abstraite

Consignes :


TP 2 – Implémenter deux types de comptes

Consignes :


TP 3 – Utilisation polymorphique avec List

Consignes :

  1. Créer une List<Compte>
  2. Ajouter différents types de comptes
  3. Appliquer une même opération
  4. Observer les comportements différents

TP 4 – Gestion des comptes par numéro avec Map

Consignes :


Corrigés (extraits clés)

Parcours polymorphique correct

for (Compte c : comptes) {
    c.debiter(new BigDecimal("50"));
}

Erreurs fréquentes

  1. Utiliser if (instanceof ...) à la place du polymorphisme.
  2. Dupliquer la logique métier
  3. Confondre interface et classe abstraite
  4. Utiliser une List quand une Map est nécessaire
  5. Ne pas protéger l’unicité
  6. Multiplier les collections inutiles
  7. Briser l’encapsulation via des getters abusifs
  8. Penser que la POO complique inutilement
  9. Ne pas faire confiance au polymorphisme
  10. Résister à la disparition des if métier

Avancement du TP fil rouge bancaire (BankLite)

Aujourd’hui, vous avez :

Décisions structurantes :


Synthèse de la journée

Vous savez maintenant :


Préparation du Jour suivant

Le Jour 9 abordera :

```