{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chapitre 8 : \"La programmation orientée objet\" - solutions\n",
    "\n",
    "---\n",
    "\n",
    "<table class=\"appbclp1\">\n",
    "<tr> <td style=\"font-weight: bold!;\">auteurs :</td><td style=\"text-align: left!;\">Laurent Pointal &lt;<a href=\"mailtp:laurent.pointal@limsi.fr\">laurent.pointal@limsi.fr</a>&gt; &amp; Bob Cordeau &lt;<a href=\"pycours@kordeo.eu\">pycours@kordeo.eu</a>&gt; </td> </tr>\n",
    "<tr> <td style=\"font-weight: bold!;\">licence :</td><td style=\"text-align: left!;\">Creative Commons Attribution-ShareAlike 4.0 International Public License (CC BY-SA 4.0) </td> </tr>\n",
    "<tr> <td style=\"font-weight: bold!;\">source :</td><td style=\"text-align: left!;\"><a href=\"https://github.com/lpointal/appbclp\">https://github.com/lpointal/appbclp</a></td> </tr>\n",
    "<tr> <td style=\"font-weight: bold!;\">description :</td><td style=\"text-align: left!;\">Les programmes dans ce Notebook Jupyter servent de support au livre <span style=\"font-style: italic!;\">« Python 3 - Apprendre à  programmer dans l'écosystème Python»</span>, édition Dunod, dans lequel les différentes sections sont expliquées </td> </tr>\n",
    "<tr> <td style=\"font-weight: bold!;\">url :</td><td style=\"text-align: left!;\"><a href=\"https://www.dunod.com/sciences-techniques/python-3\">https://www.dunod.com/EAN/9782100809141</a></td> </tr>\n",
    "</table>\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### exo08_01"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# exo08_01\n",
    "\n",
    "from math import sqrt\n",
    "class Point:\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "    def __str__(self):\n",
    "        return \"({:f},{:f})\".format(self.x, self.y)\n",
    "    def distance_origine(self):\n",
    "        return sqrt(self.x ** 2 + self.y ** 2)\n",
    "\n",
    "p0 = Point(0,0)\n",
    "p1 = Point(3.0, 4.0)\n",
    "p2 = Point(-3.0, 4.0)\n",
    "p3 = Point(sqrt(2), -sqrt(2))\n",
    "for p in (p0, p1, p2, p3):\n",
    "    print(p, \"==>\", p.distance_origine())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### exo08_02"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# exo08_02\n",
    "\n",
    "from math import sqrt\n",
    "\n",
    "class Point:\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "    def __str__(self):\n",
    "        return \"({:f},{:f})\".format(self.x, self.y)\n",
    "    def __repr__(self):\n",
    "        return \"({:f},{:f})\".format(self.x, self.y)\n",
    "    def distance_origine(self):\n",
    "        return sqrt(self.x ** 2 + self.y ** 2)\n",
    "    def __sub__(self, other):\n",
    "        return sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)\n",
    "\n",
    "class Polygone:\n",
    "    def __init__(self, sommets):\n",
    "        if len(sommets) < 3:\n",
    "            raise ValueError(\"Le polygone doit contenir au moins 3 sommets\")\n",
    "        self.som = sommets\n",
    "    def perimetre(self):\n",
    "        total = 0\n",
    "        for i in range(len(self.som)):\n",
    "            total += self.som[i] - self.som[(i + 1) % len(self.som)]\n",
    "        return total\n",
    "    def limites(self):\n",
    "        minx = min(pt.x for pt in self.som)\n",
    "        maxx = max(pt.x for pt in self.som)\n",
    "        miny = min(pt.y for pt in self.som)\n",
    "        maxy = max(pt.y for pt in self.som)\n",
    "        return Point(minx, miny), Point(maxx, maxy)\n",
    "    def hauteur(self):\n",
    "        m, M = self.limites()\n",
    "        return M.y - m.y\n",
    "    def largeur(self):\n",
    "        m, M = self.limites()\n",
    "        return M.x - m.x\n",
    "    \n",
    "p0 = Point(0, 0)\n",
    "p1 = Point(3.0, 4.0)\n",
    "p2 = Point(-3.0, 4.0)\n",
    "p3 = Point(sqrt(2), -sqrt(2))\n",
    "print(\"p1 - p2 =\", p1 - p2)\n",
    "print(\"p3 - p0 =\", p3 - p0)\n",
    "p = Polygone([p0, p1, p2, p3])\n",
    "print(\"Périmètre :\", p.perimetre())\n",
    "print(\"Limites :\", p.limites())\n",
    "print(\"Largeur :\", p.largeur())\n",
    "print(\"Hauteur :\", p.hauteur())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### exo08_03"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# exo08_03\n",
    "\n",
    "from collections import namedtuple\n",
    "\n",
    "from collections import namedtuple\n",
    "\n",
    "Emprunt = namedtuple(\"Emprunt\", \"livre usager\")\n",
    "\n",
    "class Ouvrage:\n",
    "    def __init__(self, auteur, titre, classification):\n",
    "        self.auteur = auteur\n",
    "        self.titre = titre\n",
    "        self.classification = classification\n",
    "        \n",
    "class Livre:\n",
    "    def __init__(self, ouvrage):\n",
    "        print(\"Nouvel exemplaire de «{}» par {}.\".format(ouvrage.titre, ouvrage.auteur))\n",
    "        self.ouvrage = ouvrage\n",
    "        self.disponible = True\n",
    "        self.emprunteur = None\n",
    "    def emprunte_par(self, usager):\n",
    "        print(\"Emprunt de «{}» par {}.\".format(self.ouvrage.titre, usager.identite))\n",
    "        self.disponible = False\n",
    "        self.emrunteur = usager\n",
    "    def rendu(self):\n",
    "        print(\"Retour de «{}».\".format(self.ouvrage.titre))\n",
    "        self.disponible = True\n",
    "        self.emprunteur = None\n",
    "        \n",
    "class Usager:\n",
    "    def __init__(self, identite, adresse):\n",
    "        print(\"Nouvel usager {}\".format(identite))\n",
    "        self.identite = identite\n",
    "        self.adresse = adresse\n",
    "        self.emprunts = []\n",
    "    def emprunte(self, livre):\n",
    "        self.emprunts.append(livre)\n",
    "        livre.emprunte_par(self)\n",
    "    def rend(self, livre):\n",
    "        self.emprunts.remove(livre)\n",
    "        livre.rendu()\n",
    "\n",
    "# On crée quelques objets et leurs relations.\n",
    "o1 = Ouvrage(\"Baudelaire, Charles\", \"Le Poème du haschisch\", \"poésie\")\n",
    "o2 = Ouvrage(\"Baudelaire, Charles\", \"L’Œuvre et la vie d’Eugène Delacroix\", \"biographie\")\n",
    "o3 = Ouvrage(\"Camus, Albert\", \"L’Étranger\", \"roman\")\n",
    "o4 = Ouvrage(\"Orwell, Georges\", \"1984\", \"anticipation\")\n",
    "l1 = Livre(o1)\n",
    "l2 = Livre(o2)\n",
    "l3 = Livre(o3)\n",
    "l4 = Livre(o4)\n",
    "l5 = Livre(o4)  # 2ème exemplaire du même ouvrage\n",
    "laurent = Usager(\"Pointal, Laurent\", \"Son domicile\")\n",
    "bob = Usager(\"Cordeau, Bob\", \"Chez lui\")\n",
    "laurent.emprunte(l2)\n",
    "bob.emprunte(l4)\n",
    "laurent.emprunte(l1)\n",
    "bob.rend(l4)\n",
    "laurent.rend(l2)\n",
    "bob.emprunte(l2)\n",
    "laurent.rend(l1)\n",
    "bob.rend(l2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Notes sur cette correction\n",
    "\n",
    "Il s'agit d'une analyse très sommaire, un cas réel demanderait à se poser la question du stockage et de la relecture des données, de l'interface utilisateur, de l'indentification unique des informations, du couplage entre celles-ci dans les relations, de la gestion des erreurs, de l'historique, etc. \n",
    "\n",
    "Il faudrait aussi découper plus finement certains attributs, par exemple l'`auteur` conduirait à connaître séparément son nom et prénom, date de naissance, bibliographie… on arriverait finalement à une nouvelle classe dédiée. \n",
    "\n",
    "L'affinage du cahier des charges conduirait par ailleurs à définir d'autres fonctionnalités comme par exemple la réservation par des usagers de livres empruntés par d'autres."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### exo08_04"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# exo08_04\n",
    "\n",
    "# Définition de classe ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n",
    "class Intervalle:\n",
    "\n",
    "    def __init__(self, a, b):\n",
    "        try:\n",
    "            a = int(a)\n",
    "            b = int(b)\n",
    "        except:\n",
    "            import sys\n",
    "            raise ValueError('bornes non entières')\n",
    "\n",
    "        if a == b:\n",
    "            self.borne_inf = self.borne_sup = 0\n",
    "        else:\n",
    "            self.borne_inf = min(a, b)\n",
    "            self.borne_sup = max(a, b)\n",
    "\n",
    "    def __contains__(self, n):\n",
    "        return self.borne_inf <= n <= self.borne_sup\n",
    "\n",
    "    def __and__(self, autre):  # intersection (opérateur '&')\n",
    "        if (autre.borne_inf > self.borne_sup) or (autre.borne_sup < self.borne_inf):\n",
    "            return Intervalle(0, 0)\n",
    "        else:\n",
    "            return Intervalle(max(self.borne_inf, autre.borne_inf),\n",
    "                              min(self.borne_sup, autre.borne_sup))\n",
    "\n",
    "    def __or__(self, autre):  # union (opérateur '|')\n",
    "        if (autre.borne_inf > self.borne_sup) or (autre.borne_sup < self.borne_inf):\n",
    "            return Intervalle(0, 0)\n",
    "        else:\n",
    "            return Intervalle(min(self.borne_inf, autre.borne_inf),\n",
    "                          max(self.borne_sup, autre.borne_sup))\n",
    "\n",
    "    def __str__(self):\n",
    "        if self.borne_inf == self.borne_sup:\n",
    "            return '<vide>'\n",
    "        else:\n",
    "            return '[{}, {}]'.format(str(self.borne_inf), str(self.borne_sup))\n",
    "\n",
    "\n",
    "# Programme principal =========================================================\n",
    "i0 = Intervalle(5, 5)\n",
    "i1 = Intervalle(7, 10)\n",
    "i2 = Intervalle(-9, -5)\n",
    "i3 = Intervalle(7, 10)\n",
    "i4 = Intervalle(12, 20)\n",
    "\n",
    "print(i0)\n",
    "print(i1)\n",
    "print(i2)\n",
    "print(i3)\n",
    "print(i4)\n",
    "\n",
    "print(7 in i1)\n",
    "print(7 in i2)\n",
    "print(i1 & i3)\n",
    "print(i1 | i3)\n",
    "print(i1 & i4)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
