OOP

Une rapide introduction au monde de la programmation orientée objet avec les langages Java et C#.

Qu’est ce que l’OOP ?

Il existe des tas de manières de programmer, des styles et des formes que l’on donne au code.

Il s’agit des paradigmes de programmation: procédurale, fonctionnelle, orientée flux, par contrat, etc.

La programmation orientée objet ou POO (OOP) est:

  • Un point de vue,

  • Un outil,

  • Un paradigme,

  • Utilisé en C#, C++, Java, Ruby, Python, …

Il s’agit d’une traduction du monde réel vers un paradigme défini.

Monde réel

Procédural

Objet

Nessie est un Diplodocus et il a 12 ans.

string name = "Nessie";
string specie = "Diplodocus";
int age = 12;

ShowDinoInformation(name, specie, age);
class Dinosaur {
   +name: string
   +specie: string
   +age: int

   +roar(): string
   +sayHello(): string
}

En procédural, on observe la création de variables pour chaque dinosaure. Il s’agit d’une part de redondance de code et d’autre part d’un lien sémantique entre les variables et les traitements non explicite.

Des objets

Qu’est ce qu’un objet ?

Un objet est un truc, un machin ou encore un bidule. Sa définition est floue, pour résumer un object peut être n’importe quoi que vous décidiez de coder,

« Ce truc possède telle donnée, et fait telle chose avec »

Important

Définir une classe permet de décrire à quoi ressemble notre objet:

  • Quels sont ses attributs (sa mémoire),

  • Quels sont ses méthodes (son comportement).

@startuml dinosaur_methods
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

class Dinosaur {
    +name: string
    +specie: string
    +age: int

    +roar(): string
    +sayHello(): string
}

@enduml

Exemple de diagramme UML d’une classe dinosaure

Des packages

Note

Un package permet de regrouper des classes en ensembles appelés packages, leurs utilisations facilite la modularité, et impose une organisation à notre code.

@startuml dinosaur_package
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

package Mesozoic {

    class Dinosaur {
        -name: string
        -specie: string
        -age: int

        +roar(): string
        +sayHello(): string
        +getName(): string
        +getSpecie(): string
        +getAge(): int
        +setName(name: string): void
        +setSpecie(specie: string): void
        +setAge(age: int): void
    }

}

@enduml

La portée/Visibilitée

Il s’agit d’un mécanisme à la base de la OOP. Il permet d’indiquer l’accessibilité de nos attributs/méthodes, et par conséquencet de « cacher » des détails d’implémentations.

  • public Accessible de partout

  • protected Accessible à l’intérieur de la classe et des classes dérivées

  • private Accessible uniquement à l’intérieur de la classe

@startuml dinosaur_private
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

class Dinosaur {
    -name: string
    -specie: string
    -age: int

    +roar(): string
    +sayHello(): string
}

@enduml

Les Getter et Setter

Ils permettent respectivement d’accéder et de modifier la valeur d’un attribut. Il convient de créer un couple get/set par attribut que l’on souhaite exposer. Leurs utilisations permet d’ajouter un comportement préalable.

@startuml dinosaur_getter_setter
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

class Dinosaur {
    -name: string
    -specie: string
    -age: int

    +roar(): string
    +sayHello(): string
    +getName(): string
    +getSpecie(): string
    +getAge(): int
    +setName(name: string): void
    +setSpecie(specie: string): void
    +setAge(age: int): void
}

@enduml

Les méthodes statiques en OOP

La conception objet préconise d’utiliser des instances, toutefois il existe des cas où une instance de classe est inutile.

Le mot clé static permet à une méthode de s’éxécuter sans avoir à instancier la classe, l’appel se fait via le nom de la classe au lieu du nom de l’instance.

@startuml dinosaur_static_method
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

package Ingen {

    class Laboratory {
        +{static}createDinosaur(name: string, 
        {static}specie: string): Dinosaur
    }

}

@enduml

En général, une classe possédant des méthodes statiques n’est pas conçue pour être instanciée, mais cela ne signifie pas qu’une classe possédant une méthode statique ne doit jamais être instanciée.

Attention

Une méthode statique ne peut pas utiliser des variables d’instance qui sont par définition non statiques.

Les attributs statiques en OOP

Un attribut statique est partagée par toutes les instance de la classe.

Les attributs statiques sont initialisés lorsque la classe est chargée. Les règles utilisées pour l’initialisation d’un attribut statique sont les mêmes que pour les attributs non statiques.

Indication

  • Un attribut déclaré final ne change de plus de valeur une fois initialisé.

  • Un attribut static est utilisable sans instancier la classe.
    • Un attribut est visible « partout » si public.

Par conséquent, un attribut public static final est une constante. Par convention son nom est en majuscule.

L’héritage en OOP

Il s’agit d’une méthode de conception qui consiste à extraire les caractéristiques communes, et à les placer dans une classe plus abstraite, puis à en faire hériter les classes filles.

@startuml dinosaurs_heritage
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

package Mesozoic {


    abstract class Dinosaur {
        -name: string
        -{static}specie: string
        -age: int

        +roar(): string
        +sayHello(): string
        +getName(): string
        +getSpecie(): string
        +getAge(): int
        +setName(name: string): void
        +setSpecie(specie: string): void
        +setAge(age: int): void
    }

    class Diplodocus {

    }

    class Stegosaurus {
    }

    class TyrannosaurusRex {
    }

    class Triceratops {
    }


    Dinosaur <|-- Diplodocus 
    Dinosaur <|-- Stegosaurus
    Dinosaur <|-- TyrannosaurusRex
    Dinosaur <|-- Triceratops  
}

@enduml

Lors de la conception, il faut placer le code commun dans une classe mère. On dit que la classe fille hérite ou étend la classe mère. Les classes filles héritent les méthodes et les attributs de la classe mère.

Note

Une classe fille peut si nécessaire:

  • ajouter des attributs/méthodes qui lui sont propres,

  • redéfinir les méthodes qu’elle hérite de la classe mère.

Il reste une seule implémentation de chaque méthode à maintenir et il est possible de redéfinir une méthode dans la classe fille.

@startuml dinosaurs_heritage_overide
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

package Mesozoic {


    abstract class Dinosaur {
        -name: string
        -{static}specie: string
        -age: int

        +roar(): string
        +sayHello(): string
        +getName(): string
        +getSpecie(): string
        +getAge(): int
        +setName(name: string): void
        +setSpecie(specie: string): void
        +setAge(age: int): void
    }

    class Diplodocus {
        +roar(): string
    }

    class Stegosaurus {
        +roar(): string
    }

    class TyrannosaurusRex {
        +roar(): string
    }

    class Triceratops {
        +roar(): string
    }


    Dinosaur <|-- Diplodocus 
    Dinosaur <|-- Stegosaurus
    Dinosaur <|-- TyrannosaurusRex
    Dinosaur <|-- Triceratops  
}

@enduml

Visibilité et Héritage

Les niveaux de visibilité affectent différement l’héritage des attributs/méthodes,

  • les membres publics sont toujours accessible,

  • les membres privées ne le sont plus,

  • les membres protégées sont accessible dans la classe et les classes filles.

Quand utiliser l’héritage ?

Quand une classe est une spécialisation d’une autre, quand plusieurs classes auront le même comportement de base ou encore quand une classe réussit le test du EST-UN.

Les avantages de l’héritage sont :

  • Éviter la duplication de code,

  • Simplifier la maintenance et l’évolution du code,

  • Garantir que les classes filles possédent un comportement spécifique,

  • Toute classe fille d’une classe mère est substituable là ou le type de la classe mère est attendu, c’est le polymorphisme.

Ls interfaces en OOP

Une classe est un ensemble nom et données couplé à des opérations. Souvent on utilise uniquement une classe pour les services rendus par celle-ci i.e. à son interface.

Note

Une interface est un comportement commun entre différentes classes sans lien d’héritage.

Une interface contient uniquement des signatures de méthodes, de propriétés. Une interface contient des définitions pour un groupe de fonctionnalités connexes qu’une classe peut implémenter.

@startuml interface_alone
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

interface ITalker {
    +talk(): String
}

@enduml

Par convention les noms d’interfaces commencent par un I. Toute classe qui implémente une interface doit contenir une définition pour chaque méthode définit au sein de l’interface;

Avertissement

L’interface ne définit pas la méthode ! Elle ne fournit que la signature.

@startuml interface_classes
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

interface ITalker {
    +talk(): String
}


class Hobbit {
}

class Dwarf {
}

class Dragon {

}

ITalker <|.. Hobbit 
ITalker <|.. Dragon
ITalker <|.. Dwarf

@enduml

Important

Les membres d’une interface sont automatiquement publics, non statiques. Pour implémenter un membre d’interface, le membre correspondant de la classe doit être public, non statique et porter le même nom et la même signature.

Quand une classe implémente une interface, elle doit fournir une implémentation pour l’ensemble des membres définit au sein de l’interface.

Attention

L’interface n’offre aucune fonctionnalité dont une classe peut hériter à la manière de l’héritage.

Toutefois si une classe mère implémente une interface, alors les classes filles bénéficient de cette implémentation.

Pour aller plus loin, vous pouvez consulter les deux chapitres suivants traitant des langages Java et C#.