Programmation Client II¶

La programmation orienté objet - partie 3¶

Language plus fortement typé¶

Faits : Javascript est un language dynamiquement typé¶

function add(a, b){
    return a + b;
}
  • La fonction add peut être appelé avec n'importe quoi
    • C'est au programmeur de s'assurer que les arguments passé a add "font du sense"

Faits : Javascript est un language dynamiquement typé¶

let x = 10;

x = doSomethingToIntegerAndReturnAString(x);

doSomethingToAString(x);
  • Le type des variable n'est pas forcément constant

  • Dans l'exemple ci-haut, x passe d'un number à un string

Opinion: Javascript a "peu" de syntaxe... mais délègue beaucoup validation au programmeur¶

Dans un programme javascript, le programmeur doit s'assurer que tous les paramêtre que les fonctions recoivent font du sense.

let x = generateX();
let y = generateY();
let z = mergeXAndY(x, y);
z = incrementZ(z);
printAll(x, y, z);

Idée de génie¶

Les "Si"¶

  • Si nous étions capable de décrire ce que les fonctions peuvent prendre en arguments
  • Si les fonctions nous décrivent ce qu'elles retourne comme valeurs
  • Si nous somme capable de décrire nos variables

Alors¶

Nous serions capable de savoir au compile time (sans executer le code) si notre programme est valide

Exemple¶

let x = generateX();      // generateX retourne un "number"
let y = generateY();      // generateY retourne un "String" 
let z = mergeXAndY(x, y); // mergeXAndY prend un "number" et 
                          // un "string" et retourne un "number"
z = incrementZ(z);        // incrementZ prend un "number" et 
                          // retourne un "number"
printAll(x, y, z);        // prend un number, un string et un number

Vérification de l'exemple¶

  • Nous pourrions écrire un programme qui vérifie que le programme "fait du sense"

Contre exemple¶

Si let z = mergeXAndY(x, y) retournait un String alors on ne pourrait pas faire z = incrementZ(z);

TypeScript¶

Présentement, le language le plus populaire pour "typer du javascript" est TypeScript:

function generateX(): number {
    return 22;
}

function generateY(): String {
    return "hello";
}

function mergeXAndY(x: number, y: String): number {
    return x + y.length;
}

function incrementZ(z: number): number{
    return z + 908;
}

function printAll(x: number, y: String, z: number): void {
    console.log(`number: ${x} , number: ${ y},  number: ${z}`);
}

let x = generateX();
let y = generateY();
let z = mergeXAndY(x, y);
z = incrementZ(z);
printAll(x, y, z);

Pratiquement, utiliser typescript require un peu (beaucoup?) de travaille¶

flowchart LR
    TypeScript --vérification--> JavaScript
    JavaScript --> Browser

OOP avec languages plus fortement typé¶

Dans les languages OOP fortement typé classiques (Java, C#,... mais surtout Java 1.1), les fonctions utilises les classes pour décrire ce qu'elles peuvent recevoir en argument et ce qu'elles retournes.

function getBestTeamName(team1: Team, team2: Team): String {

}

Probleme: utiliser une fonction avec un autre type¶

function getBestTeamName(team1: Team, team2: Team): String {

}

const les3 = new DodgeballTeam("Les 3");
const les5 = new DodgeballTeam("Les 5");

console.log(getBestTeamName(les3, les5)); // Erreur!  DodgeballTeam n'est pas un Team

Solution¶

Il faut donc être capable de dire qu'un DodgeballTeam est un Team

Sous classes¶

Continuons avec notre exemple (mais en javascript)¶

Ce que nous voulons:¶

flowchart TD
    Team --> DodgeballTeam
  1. On veut créer une classe DodgeballTeam qui est pratiquemement la classe Team plus des extra

  2. Partout où une instance de Team est acceptée, il faut qu'une instance de DodgeballTeam soit acceptée.

Construisions une classe Team¶

class Team {
    constructor(partiesGagnes, partiesPerdues){
        this.partiesGagnes = partiesGagnes;
        this.partiesPerdues = partiesPerdues;
    }

    getDiff(){
        return this.partiesGagnes - this.partiesPerdues;
    }
}

Sous-Classe DodgeballTeam¶

class DodgeballTeam extends Team {
    constructor(partiesGagnes, partiesPerdues, ballonsAttrapes){
        super(partiesGagnes, partiesPerdues);
        this.ballonsAttrapes = ballonsAttrapes
    }

    ballonsAttrapesParParties(){
        let parties = this.partiesGagnes + this.partiesPerdues;
        return this.ballonsAttrapes / parties;
    }
}

Mots clées:

extends: est le mot magique pour créer une sous classe.

Le mot réservé super¶

  • Est l'équivalent du mot this mais parle de la classe du haut.

  • this est à propos de la classe présente ou parent...

Exemple du mot super¶

class Foo {
   foo(){
       return 10;
   }
}

class Bar extends Foo{
   foo(){
       return 1000;
   }

   bar(){
       return super.foo();
   }
}

const bar = new Bar();
console.log(bar.foo()) // 1000
console.log(bar.bar()) // 10

Terminologies¶

  • sous-classe (sub-class) didier aime beaucoup ce terme. Le "sous" est entendu au point de vue mathématiques; un sous groupe d'un groupe libre est... libre!

  • super-classe (super-class) didier accepte ce terme par manque d'un meilleur terme. Le terme "super" veut dire la classe parente direct

  • classe enfant (child class) didier n'aime vraiment pas ce terme.
    Fuck la patriachie

  • classe parent (parent class) didier n'aime vraiment pas ce terme.
    Fuck la patriachie

  • polymorphisme est le concept consistant à fournir une interface unique à des entités pouvant avoir différents types. --- wikipedia

Exercices Pédagogiques!¶

Note à didier¶

  • implément les sous classes live avec les étudiants

Créer une sous-classe Carré!¶

class Rectangle {

  constructor(hauteur, largeur) {
    this.hauteur = hauteur;
    this.largeur = largeur;
  }

  aire() {
    return this.hauteur * this.largeur;
  }
}
  1. Créer une sous-classe de Rectanglenommé Carre
  2. Ajouter la méthode perimetre à Carre

Solution¶

class Carre extends Rectangle {
  constructor(cote) {
    super(cote, cote);
  }

  perimetre(){
      return 4 * this.hauteur
  }
}
  • Pour appeler le constructeur super dans une sous classe est d'utiliser super().

  • On peut façilement ajouter une fonction dans une sous-classe

Créer une sous-classe Carré!¶

class User {

  constructor(id, username) {
    this.id = id;
    this.username = username;
  }

  idAdmin() {
    return false;
  }
}
  1. Créer une sous-classe de Usernommé Admin
  2. tout admin doit être isAdmin

Solution¶

class Admin extends User {
  idAdmin() {
    return true;
  }
}
  • On peut override une méthode dans une sous-classe.

Créer une sous-classe d'animal!¶

class Animal {

  constructor() {
    this.state = "arret";
  }

  courrir() {
    this.state = "courrir";
  }

  arret() {
    this.state = "arret";
  }

  getState(){
      return this.state
  }
}
  1. Créer une sous-classe d'Animal nommé Lapin
  2. Un lapin doit être capable de se cacher
  3. Si un lapin est caché, il ne peut se mettre à courrir.

Solution¶

class Lapin extends Animal {

    constructor() {
        super();
        this.isHidden = false;
    }

    hide(){
        this.isHidden = true;
    }

    unHide(){
        this.isHidden = false;
    }

    courrir(){
        if(this.isHidden){
            return;
        }
        super.courrir();
    }
}

Note sur les solutions¶

  1. Un sous-constructeur devrait toujours appeler super

  2. Nous ne devrillions pratiquement jamais overrider une méthode

  3. Lorsque nous overriderons une méthode, nous devrillions toujours appeler la super méthode

Note importante¶

  • Tout comme les petites sections de vos brin d'ADN, une sous-classe peut avoir une seule super classe.

Deux petits "design pattern"¶

Factory!¶

On créer une fonction qui retourne différent type d'objets dépendamment de ce que la fonction prend en argument.

function getUser(loggedInRequest){
    if(request.isAdmin){
        return new Admin(request.id, request.username);
    }
    return new User(request.id, request.username);
}

instanceof¶

Pour savoir si une instance x est instance de la class Foo il faut utiliser...

let foo = new Foo();
let bar = new Bar();

foo instanceof Foo; 
bar instanceof Bar; // true ssi Bar est une sous-classe de Foo

Note poche...¶

L'héritage est un peu dépasser¶

  • Les frameworks modernes utilisent de moins en moins l'héritage

  • Les nouveaux languages de programmation ne permettent pas l'héritage

  • Les principes "SOLID" font en sorte que l'utilisation de l'héritages est assez restrain

L'héritage s'est bizzare..¶

  1. Personnellement, ça me prend énormément plus de temps lire du code qui utilise l'héritage
  2. "Mais pourquoi?" est la question principale que je me pose à propos de l'héritage
  3. L'héritage est une mauvaise manière de partager du code
  4. L'héritage en javascript est encore pire que dans des languages plus fortement typé

Exemples: React / Angular¶

  • https://react.dev/reference/react/Component

  • https://angular.io/api/core/Component

Interfaces¶

Dans les languages plus fortement typé, l'héritage à été remplacé par les "interfaces"

flowchart TD
    Team --> DodgeballTeam 
    DodgeballTeam --> MixedDodgeballTeam

Quoi faire au lieu d'utiliser de l'héritage¶

Cas d'école¶

Regardons le cas d'une équipe de dodgeball mixte. Dans une équipe de dodgeball mixte, il faut absolutment qu'au moins la moité de l'équipe soit des femmes

On commence avec la classe équipe...¶

class Team {
    constructor(partiesGagnes, partiesPerdues){
        this.partiesGagnes = partiesGagnes;
        this.partiesPerdues = partiesPerdues;
    }

    getDiff(){
        return this.partiesGagnes - this.partiesPerdues;
    }
}

Pis on renomme la classe avec un nom qui fait du sense¶

class GameStats {
    constructor(partiesGagnes, partiesPerdues){
        this.partiesGagnes = partiesGagnes;
        this.partiesPerdues = partiesPerdues;
    }

    getDiff(){
        return this.partiesGagnes - this.partiesPerdues;
    }
}
  • Lorsqu'une classe est faite pour être une super classe, souvant son nom fait aucun sense. (exemple, CoreUser, UserBase, AbstractUser, ...)

On a aussi une classe joueurs¶

class Players {
    constructor(players){
        this.players = players;
    }

    totalNumberOfPlayer(){
        let ret = 0;
        for(const player of this.players){
            if(!player.isAngered){
                ret += 1;
            }
        }
        return ret;
    }

    totalNumberOfFemalePlayer(){
        let ret = 0;
        for(const player of this.players){
            if(!player.isAngered && player.gender === "female"){
                ret += 1;
            }
        }
        return ret;
    }
}

La classe MixedDodgeballTeam...¶

class MixedDodgeballTeam{
    constructor(players, partiesGagnes, partiesPerdues){
        this.players = players;
        this.stats = new GameStats(partiesGagnes, partiesPerdues);
    }

    getDiff(){
        return this.stats.getDiff();
    }

    isValid(){
        return this.players.totalNumberOfPlayer() < 2 * this.players.totalNumberOfFemalePlayer()
    }
}

Ressources extérieures¶

Liens externes¶

  • https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes (très technique)

  • https://exploringjs.com/es6/ch_core-features.html#sec_from-constr-to-class (très technique)

  • https://youtu.be/2HAPViIAYjc

  • https://youtu.be/KjXBPJZPmYU

  • https://youtu.be/_cgBvtYT3fQ