GNU General Public License v3.0 licensed. Source available on github.com/zifeo/EPFL.
Spring 2014: Pratique de la Programmation Orientée Object
[TOC]
- les paquetages
- nom unique
- nom qualifié :
collections.Map
- visible dans le même paquetage
- interdiction de redéfinir un même nom au sein d'un paquetage ou d'un import
- import multiple peu conseillé :
import java.util.*;
- visibilité des classes dans un paquetage :
- rien : visible uniquement dans le même paquetage
- protected : visible dans leur paquetage et dans toutes les sous-classes
- private
- public
- nommage :
ch.epfl.[...]
- import statique peut être utilisé pour utiliser une methode sans nommé sa classe :
import static Math.sin;
- les assertions
- peut être supprimé à la compilation (
-ea
= enable assertion) - vérifier la validité de certaine condition
assert l > l0 || h < h0 : "l="+l+" h="+h;
- les exceptions sont plutôt utilisées pour vérifier les données saisies par l'utilisateur
- peut être supprimé à la compilation (
- le test unitaire
- pour tester automatique des entités uniques
@Test
devant la méthode et utiliserassertEquals
,assertTrue
,assertNull
deimport static org.junit.Assert.*;
@Test(expected = ArithmeticException.class)
(toujours le .class)- vérification probalisitique
- "Le test peut être utilisé pour démontrer la présence de problèmes, mais jamais pour démontrer leur absence !" Dijkstra
- énumaration
- facilite la comprehension
public enum Suit {SPADES, DIAMONDS, CLUBS, HEARTS};
- Java transforme cela en classe
Suit.values() => {SPADES, DIAMONDS, CLUBS, HEARTS}
SPADES.ordinal() => 0
toString
,compareTo
,equals
,hashCode
- possible d'ajouter nos propres methodes
switch
(toujours avec un cas default)- constructeur :
private Suit(String frenchName) {this.frenchName = frenchName;}
- variables au constructeur :
public enum Suit {SPADES("piques");}
- variables :
public enum Suit {private final String frenchName;}
- methode :
public enum Suit {public String frenchName() {return frenchName;}}
- class traduite toujours statique
- classes imbriquées statiques
public class LinkedIntStack{ private static class Node {}}
- builder
- accès aux variables statiques de la classe englobant (y compris les prives)
- annotations
- toujours attachée à une déclaration (classe, interface, variable, methode, etc.)
- peut avoir des paramètres entre parenthèse (voir test unitaire)
@Deprecated
si la methode est destinee à ne plus être utilisée@Override
pour redéfinir une methode (à toujours utiliser)@SuppressWarnings
supprime les alertes du compilateur
- collections
- structure de données abstraite
- tableaux, listes, piles, queues, ensembles, tables associations, etc.
- généricité
- permet à une classe de gérer plusieurs types sans redefinir les methodes à chaque fois
interface Stack<E> {public void addFirst(E value);
avec le paramètre E- utilisation du diamant depuis Java 7
Stack<Character> s = new LinkedStack<>();
- paramètres multiples possibles :
public final class Pair<F, S>
- les methodes peuvent aussi être génériques :
static <E> Stack<E> newLinkedStack(E e)
- des fois il faut spécifier les arguments de type au méthode générique :
Stacks.<String>newLinkedStack("hello")
- généricité et types de base
boolean, byte, short, int, long, char, float, double
impossibleBoolean, Character, Integer, Double
emballage des types de base- emballage/deballage peut être automatique ou appellé via les methodes
- limitations de la généricité en Java
- interdiction pour des raisons historiques
- creation de tableau avec elements génériques
- tests d'instance (instanceof) impliquant des types génériques
- transtypages (casts) sur des types génériques ne sont pas sûr
- définition d'exceptions génériques
- interdiction pour des raisons historiques
- collections génériques de Java
- pour chaque collection dans
java.util.
il existe une interface et des mises en oeuvres - Collection
- List (ordonnée, taille variable)
- LinkedList
- ArrayList
- Set (ensemble non ordonnée (l'ordre peut varier), pas de duplicata)
- HashSet
- TreeSet
- List (ordonnée, taille variable)
- Map (table associative, élàments indexés par des clefs d'un certain type)
- TreeMap
- HashMap
- pour chaque collection dans
- listes
- LinkedList
- noeufs chainés entre-eux
- un noeud de tête
- accès séquentiel (doit connaître les noeuds précédents pour accéder à un élément)
- peut être circulaire et doublement liée (prochain/précdent noeud)
- ajout & suppression
$O(1)$ / indexation$O(n)$ / taille$O(1)$
- ArrayList
- stocke les élements dans un tableau redimensionnable
- copie le tableau quand redimensionné
- ajout & suppression
$O()$ / indexation$O(1)$ / taille$O(1)$
- parcourir avec un for each :
for (Node n : list)
sinon le temps des listes chaînées est$O(n^2)$ - intertion & suppression
$O(n)$ / appartenance$On)$
- LinkedList
- itérateur
- permet de se déplacer sur une liste à l'aide d'un curseur
-
hasNext()
vérifie s'il existe bel et bien un prochain -
next()
retourne l'élement et avance le curseur -
remove()
supprime l'élement précédent Iterator<String> i = l.iterator(); while (i.hasNext()) { String s = i.next();}
- ensembles
- HashSet
- table de hachage
- elements doivent être transformable en entier
- fonction de hachage doit être le plus unique possible et doit toujours retourner la même valeur (hashCode)
- si les elements hachés sont bien répartis, on profite de
$O(1)$ - ajout & suppression
$O(1)$ / appartenance$O(1)$
- TreeSet
- arbre binaire
- elements doivent être comparable
- du plus petit ou plus grand
- intertion & suppression
$O(\log n)$ / appartenance$O(\log n)$
- HashSet
- equals & hashCode
- doivent être compatible
$x.equals(y)\Rightarrow x.hashCode()==y.hashCode()$ (pas forcément réciproque)
- doivent être compatible
- interface Comparable
-
x.compareTo(y)
retourne un entier- négatif si
$x$ est strictement inférieur à$y$ - 0 si égaux
- positif si
$x$ est strictement supérieur à$y$
- négatif si
- compareTo et equals doivent être aussi compatible
$x.equals(y) \iff x.compareTo(y)$
-
- tables associatives
- même chose que pour les ensembles
- itération sur les clés :
set.keySet()
- itération sur les valeurs :
set.values()
- itération sur les pairs :
set.entrySet()
(de typeEntry
,.getKey()
,.getValue()
,.setValue()
)
- création
- capcité initial définie ou dans le constructeur
- redimensionnement
- lorsque le tableau est plein, on en recrée un plus grand par copie
- intertion/suppression
- décaler les élements
- ne pas oublier d'effacer les éléments décalés qui seraient à double
- interface Iterable
- besoin d'avoir la methode
iterator()
redéfinie - permet de rendre la classe itérable grâce à la boucle for each
- besoin d'avoir la methode
- interface Set
boolean isEmpty();
int size();
void add(E elem);
void remove(E elem);
boolean contains(E elem);
Iterator<E> iterator();
- ListSet
- ajout oblige la verification de duplicata
$O(n)$ - suppression
$O(n)$ et peut ne rien supprimer - appartenance
$O(n)$ car simple parcourt de tous les elements
- ajout oblige la verification de duplicata
- HashSet
- l'efficacité dépend de la fonction de hachage
- les elements se trouvent dans des listes determinées par le hachage
- on essaie d'avoir des listes avec une moyenne proche de 1 par liste, c'est le facteur de charge
- on peut rehacher pour avoir un meilleur facteur de hachage
- classe imbriquée non statique
- a accès à tous les membres de la classe englobante (y compris privés)
- classe imbriquée anonyme
return new Iterator<E>() {private int remaining = size;};
- constructeur
new Iterator<E>() {{remaining = size;}};
- a accès aux variables locales si elles sont déclarés finales
- tables associatives
- listes associatives
ListMap
- paires clef/valeur appelées entries
- méthode pratique entryFor car souvent parcourt
$O(n)$ - ajout vérifie l'existence et remplace, ou ajoute
- remove si trouvé
- get si trouvé
- méthode pratique entryFor car souvent parcourt
- tables de hachage
HashMap
- arbre binaires de recherche `TreeMap
- iterable seulement sur les entry, clef ou valeur
- incompatibilité encore Entry et Map.Entry
- paires clef/valeur appelées entries
- listes associatives
- patrons de conception : solutions à des problèmes généraux
- nom, description du problème résolu, description de la solution et des conséquences de l'utilisation
- diagramme de classe
- héritage : flèche grasse
- association (utilisation) : flèche fine
- instanciation (crée) : flèche traitillée
- Iterator ou Cursor
- problème : exposer la representation interne sans violé les principes de l'encapsulation
- solution : méthode permettant d'obtenir un objet d'une collection et d'avancer vers un suivant
- Builder
- problème : creation d'objet immuables, pas facile d'avoir un gros constructeur
- solution : proposer une classe qui construit un objet
- cela permet le contrôle des données et la mise en place de caractéristiques automatiques
- Factory Method
- problème : permettre l'extension en créant des sous-classes n'assure pas que la sous-classe soit utilisé par le programme
- solution : une méthode fabrique qui regroupe la construction de l'objet
- Abstract Factory
- même chose mais regroupe les méthodes fabriques dans une classe externe et abstraite
- Adapter
- problème : rendre compatible des méthodes pour d'autres structures de données
- solution : écrire une classe qui adapte les données et qui sert interface d'adaptation
- Observer :
- problème : mettre à jour une donnée basé sur une autre classe/instance
- solution : permettre d'observer et d'être informé entre les classes (observer et subject)
- attention aux cycles de dépendances et glitches
- MVC
- problème : séparer et classer les bouts de code
- solution : découper le code entre trois partie (modèle, vue, contrôleur)
- Strategy ou Policy
- problème : spécifier une action en fonction de la variable d'entrée (tri, comment trier ?)
- solution : permettre la spécification d'un objet s'appliquant sur les variables d'entrées (comparateur)
- Decorator ou Wrapper
- problème : comment modifier à la volée l'effet d'une méthode
- solution : appliquer une transformation (autre méthode) au résultat de la méthode (comparateur inverse) principe de l'emballage
- flots java symbolisé par le paquetage
java.io
- accès séquentiel
- input vs output streams
- flots de caractères :
Reader
etWriter
- flots d'octets :
InputStream
etOutputStream
- possibilité de décorer ces flots (compression, cryptage, tampon)
- Composite
- problème : comment représenter les ordres composés de plusieurs critères
- solution : définir un macro-comparateur et une hiérarchie des comparaisons
- arisé des méthodes
- ìnt sum(int... vs)` permet la génération d'un tableau avec le nombre d'élément donné
- on peut avec des arguments obligatoires (spécification avant les ...)
- héritage
- mal utilisé
- compter le nombre d'éléments ajouter : la résolution dynamique des liens empêche une solution sur si on utilise l'héritage
- on a meilleur temps de décorer l'ensemble et est hérité dans une autre classe qui redéfini les méthodes d'ajouts
- classe instanciable ne sont pas fait pour être hérité, on les mets final
- classe héritable est une classe dont le rôle est de servir de super-classe
- modifiabilité
- classe muable ne sont pas faites pour être stocké (hashcode) et modifiée dans des collections
- avantage aux classes immuables (sinon il faut redéfinir clone, etc.)
- Les instances d'une classe non modifiable doivent être comparées structurellement, celles d'une classe modifiable doivent l'être par identité
- désavantage de la mutabilité face à la concurrence, pas facile de raisonner correctement
- par défaut immuable
- final à tous les champs
- pas de setters
- pas de getters modifiable
- copie profonde
- collections peuvent être non modifiable
- coûteux, mais pas tant, en effet, elles peuvent partager les noeuds car elles sont non modifiables
- permet les systèmes de version facilement
- sous-typage
- réflexif (sous-type de lui-même), transitif (sous-type est sous-type d'un sous-type de parent) et anti-symétrique (sous-type entre eux = même objet)
- ordre partiel
- polymorphisme d'inclusion est le principe de substitution de type
- généricité
- valide d'ajouter un sous-type à une collection générique cas par cas
- impossible d'ajouter en bloc une collection car un instanciation d'un type générique n'est jamais un sous-type
- possible de définir addall quand même en rendant la méthode générique et bornée
- jokers (wildcards) : pas besoin de nommer une généricité si elle est utilisée qu'une seule fois, on peut mettre un joker
?
- borne inférieur
<? super E>
uniquement pour les jokers - lois des bornes PECS (Producer Extends, Consumer Super)
- quand on lit dans une structure, on utilise une borne supérieure (extends)
- quand on écrit dans une structure, on utilise une borne inférieure (super)
- quand on fait les deux, pas de borne
- types brutes jamais sauf vieille collection ou
List
était valide avant la généricité
- gestion mémoire
- ramasse miettre
- ne pas oublier de mettre à null en cas de remove