public class Conception {}

/* La phase de conception intervient une fois complétée l'analyse du système
 * à construire, et définit les structures de données et l'organisation du
 * code. La phase d'analyse s'intéressait au "quoi", la phase de conception
 * se concentre sur le "comment".
 */

/* I. Les attributs */
class Complexe {
    /* Prenons une classe représentant les nombres complexes. La phase
     * d'analyse lui a associé quatre attributs :
     * - partie réelle [r]
     * - partie imaginaire [img]
     * - module [rho]
     * - argument [theta]
     * Ces arguments ne sont pas indépendants les uns des autres. 
     */
    private double r, img, rho, theta;

    /* Un premier constructeur : il prend en paramètre une partie réelle
     * et une partie imaginaire, et en déduit le module et l'argument.
     */
    public Complexe(double r, double i) {
	this.r = r;
	this.img = i;
	updateRho();
	if (r!=0) this.theta = Math.arctan(i/r);
	else this.theta=Math.PI/2.0;
    }

    /* Dans de telles conditions, toute modification de l'un des attributs
     * doit être reflétée sur les autes.
     * Dans le vocabulaire du cours précédent, la relation entre les quatre
     * attributs [r], [img], [rho] et [theta] est un invariant de notre classe
     * [Complexe].
     * Une des raisons pour lesquelles je vous ai toujours dit beaucoup
     * de mal des "setters" (méthodes publiques qui permettent de modifier la
     * valeur d'un attribut) est qu'ils risquent d'invalider les invariants
     * d'un objet s'ils sont mal écrits.
     * En l'occurrence, si l'on veut fournir un setter pour la partie
     * imaginaire d'un nombre complexe, alors il ne faut pas modifier
     * uniquement l'attribut [img], mais aussi mettre à jour [rho] et [theta]
     * en conséquence.
     */
    public void setImg(double i) {
	this.img = i;
	updateRho();
	this.theta = ...
    }

    private void updateRho() {
	this.rho = Math.sqrt(this.r*this.r + this.img*this.img);
    }

    /* Dans cette version nous avons donc :
     * - plusieurs attributs
     * - un accès immédiat aux valeurs de ces attributs
     * - un coût à payer pour faire des mises à jour à chaque fois que
     *   l'un des attributs est modifié
     */

}

/* On peut éviter ce genre de mises à jour si on élimine les informations
 * redondantes dans la classe. Par exemple, on peut ne donner à une classe
 * [Complexe] que les attributs [r] et [img], et calculer les valeurs de [rho]
 * et [theta] uniquement à la demande. On dit alors que [rho] et [theta] sont
 * des attributs dérivés.
 */
class Complexe {
    /* Deux attributs */
    private double r, img;

    /* Un seul constructeur */
    public Complexe(double r, double i) {
	this.r = r;
	this.img = i;
    }

    /* Un modificateur qui ne casse aucun invariant */
    public void setImg(double i) {
	this.img = i;
    }

    /* Un getter qui calcule à la volée la valeur de "l'attribut" qu'il
     * représente.
     */
    public double getRho() {
	return Math.sqrt(this.r*this.r + this.img*this.img);
    }

    /* Dans cette version nous avons donc :
     * - des attributs réels et des attributs dérivés
     * - une possibilité de modifier facilement les valeurs des attributs réels
     * - un coût à payer pour accéder aux valeurs des attributs secondaires
     * Le choix entre les deux versions peut dépendre de l'usage attendu
     * de la classe concernée, pour essayer de minimiser le coût des opérations
     * les plus courantes.
     */
}


/* Digression : une variante dans laquelle on ajoute à chaque opération
 * de lecture ou de modification un coût modique pour éviter les surcoûts
 * importants sur une catégorie ou l'autre.
 */
class Complexe {
    /* On prend les quatre attributs et on ajoute un booléen qui indique si
     * la valeur de l'un des attributs a été modifiée.
     */ 
    private double r, img, rho, theta;
    private boolean change;

    public Complexe(double r, double i) {
	this.r = r;
	this.img = i;
	updateRho();
	if (r!=0) this.theta = Math.arctan(i/r);
	else this.theta=Math.PI/2.0;
    }

    /* Lors de la modification de [img] on ne met pas à jour les autres
     * attributs, mais on signale qu'un changement a été apportée avec 
     * le booléen [change].
     */
    public void setImg(double i) {
	this.img = i;
	this.change = true;
    }

    /* Lors de la lecture de [rho] on commence par consulter l'attribut
     * [change] pour savoir si la valeur de [rho] est encore à jour ou si elle
     * doit être recalculée.
     */
    public double getRho() {
	if (!this.change) { return this.rho; }
	else {
	    updateRho(); updateTheta();
	    this.change=false;
	    return this.rho;
	}
    }
    
    void updateRho() {
	this.rho = Math.sqrt(this.r*this.r + this.img*this.img);
    }
}


/* Reprenons la classe d'origine avec son constructeur. */
class Complexe {
    private double r, img, rho, theta;

    public Complexe(double r, double i) {
	this.r = r;
	this.img = i;
	updateRho();
	if (r!=0) this.theta = Math.arctan(i/r);
	else this.theta=Math.PI/2.0;
    }

    /* On pourrait vouloir écrire un deuxième constructeur basé sur le
     * module et l'argument.
     * Ce n'est pas possible : le mécanisme de surcharge est basé sur le type
     * des paramètres, qui sont identiques pour nos deux constructeurs.
     */
    public Complexe(double rho, double theta) {
	this.rho = rho;
	this.theta = theta;
	...
    }

    /* Une tentative pour donner néanmoins les deux possibilités : un
     * constructeur qui prend en troisième paramètre un booléen indiquant
     * un choix de coordonnées cartésiennes ou polaires.
     */
    public Complexe(double p1, double p2, boolean cart) {
	if (cart) {
	    this.r = r;
	    this.img = i;
	    updateRho();
	    if (r!=0) this.theta = Math.arctan(i/r);
	    else this.theta=Math.PI/2.0;
	} else {
	    // Initialisation en coordonnées polaires.
	}	    
    }

    /* On peut faire mieux avec le design pattern "factory", voir
     * classe suivante.
     */
}

/* Design pattern "factory" : donner plusieurs manières de construire
 * un objet, en les distinguant de manière plus explicite que par le seul
 * mécanisme de surcharge.
 */
class Complexe {
    private double r, img, rho, theta;

    /* Le constructeur est indiqué "privé" : il n'est pas directement
     * accessible de l'extérieur.
     */
    private Complexe(double r, double i) {
	this.r = r;
	this.img = i;
	updateRho();
	if (r!=0) this.theta = Math.arctan(i/r);
	else this.theta=Math.PI/2.0;
    }

    /* À la place, on fournit deux méthodes aux noms évocateurs, qui
     * appellent le constructeur et renvoie l'objet construit.
     * L'utilisation du mot-clé [static] sera expliquée au prochain cours.
     */
    public static Complexe creeComplexeCartesien(double r, double i) {
	return new Complexe(r, i);
    }
    public static Complexe creeComplexePolaire(double rho, double theta) {
	double r = ...;
	double i = ...;
	return new Complexe(r, i);
    }
}


/* II. Les associations */

/* Les associations peuvent être réalisées de multiples façons.
 * On donne ici quelques exemples en fonction de l'arité de l'association
 * et de la navigabilité souhaitée.
 * Ces exemples sont des bonnes pratiques car ils font en sorte que les arités
 * soient respectées en permanence, mais ils ne sont pas les seules manières
 * de faire.
 */ 


/* II.A. Les associations unidirectionnelles */

/* On parle d'une association unidirectionnelle lorsque la navigation se fait
 * toujours dans la même direction. Dans les exemples suivants, un objet de
 * la classe [A] pourra accéder à un (ou des) objet de la classe [B].
 * C'est le cas typique de la délégation.
 */

/* Association unidirectionnelle, arité 1-1 */
class A {
    private B b;
    public A() {
	/* Création d'un objet B privé directement à l'intérieur de
	 * la classe : personne d'autre n'y a accès.
	 */
	this.b = new B();
    }
}

class B {}


/* Association unidirectionnelle, arité *-1 */
class A {
    private B b;
    /* Ici, l'objet B est pris en paramètre : il a été défini a l'extérieur
     * et peut être lié à d'autres objets A.
     */
    public A(B b) {
	this.b = b;
    }
}

class B {}


/* Association unidirectionnelle, arité *-* */
class A {
    /* Un objet de la classe [A] est associé à une collection d'objets de la
     * classe [B]. Cette collection peut être ordonnée ou non, avec répétitions
     * ou non, est donc être réalisée par différentes structures de données.
     */
    private Set bs; // Alternative : private List bs;
    public A() {
	/* Création d'un ensemble vide */
	this.bs = new HashSet();
    }
    /* Ajout à la collection d'un élément [B] défini à l'extérieur. */
    public void addB(B b) {
	bs.add(b);
    }
}

class B {}


/* II.B. Les associations bidirectionnelles. */

/* On parle d'une association bidirectionnelle lorsque la navigation peut se
 * faire dans les deux sens. Dans les exemples suivants, un objet de la classe
 * [A] pourra accéder à un (ou des) objet de la classe [B], qui pourra lui-même
 * accéder à l'objet [A] en retour.
 */

/* Association bidirectionnelle, arité 1-1 */

class A {
    /* Association représentée par un attribut */
    private B b;

    public A() {
	/* Le constructeur crée un [B] de manière à ce qu'il soit associé à
	 * l'objet A qu'on est en train de créer.
	 */
	this.b = new B(this);
    }
}

class B {
    /* Cette fois, un objet [B] a lui aussi un objet [A] en attribut. */
    private A a;

    /* Le constructeur prend en paramètre l'objet [A] auquel il faut
     * s'associer.
     */
    public B(A a) {
	this.a = a;
    }
}

/* La version que nous venons de voir est asymétrique : c'est en créant l'objet
 * [A] que l'on crée la paire d'objets associés. On peut rendre la situation
 * plus symétrique en ajoutant un constructeur [public A(B b)] à [A] et un
 * constructeur [public B()] à [A].
 */

/* Association bidirectionnelle, arité 1-* */

class A {
    private List bs;
    public A() {
	/* Initialisation avec une liste vide. */
	this.bs = new ArrayList();
    }
    /* L'ajout d'un élément [b] à la liste implique aussi de mettre à jour
     * cet objet [b].
     */
    public void addB(B b) {
	/* Ajoute b à notre liste. */
	this.bs.add(b);
	/* Pour l'instant l'état n'est pas cohérent :
	 * b.a n'a pas été mis à jour.
	 */
	b.setA(this);
	/* Maintenant c'est bon. */
    }
    /* Cette méthode sera appelée par [B.setA]. */
    protected void removeB(B b) {
	this.bs.remove(b);
    }
}

class B {
    private A a;
    protected void setA(A a) {
	/* Quand on ajoute l'objet à la liste d'un [A], il faut aussi supprimer
	 * l'éventuelle ancienne association à un autre [A].
	 * Dans le cas général, tester d'abord a!=null.
	 */
	this.a.removeB(this);
	/* Enfin, on peut faire la mise à jour proprement dite. */
	this.a = a;
    }
}

/* Exercice : faire une association bidirectionnelle *-* */