Include images :D

This commit is contained in:
Fred Pauchet 2020-11-28 21:57:42 +01:00
parent cf1a7f2c1a
commit ca4451cc24
9 changed files with 49 additions and 32 deletions

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2020-11-25T20:34:43.196Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.9.9 Chrome/85.0.4183.121 Electron/10.1.5 Safari/537.36" etag="zlmuIPRUvmCwMK8GC-SV" version="13.9.9" type="device"><diagram id="c8VOhFbdkK45Rh3s6dRX" name="Page-1">3Vpbc6M2FP41fqwHEBf7sbaT7EO2m123m92+ZBQhgxqMGCF82V9fCYQBC9skNXbHM5kMOuj6fYdP50gegOly88BgEn6mPo4GluFvBmA2sCzTtqyB/DP8bWEZmW5hCBjxVaXKMCe/sDIaypoRH6eNipzSiJOkaUQ0jjHiDRtkjK6b1RY0ao6awABrhjmCkW59Jj4P1Sosr7J/wiQIy5FNd1y8WcKyslpJGkKfrmsmcDcAU0YpL56WmymOJHglLkW7+wNvdxNjOOZdGsxXJhh9//uBfsPe81/JV373p/GbrbpZwShTK1az5dsSAkaz2MeyF3MAJpAhxZKgCEx8mIb5O1lYkCia0ogyUY5pLCpNAgZ9Ima4Z045o287RC1pecMchWoQNSnMON4cXK65A1F4H6ZLzNlWVFENbAW78jtzPPScwrKueLTLWmGNQ+AYQ1UVKu8Jdt1XAIsHhfF78LZO492ANOTLSIEiHCiRVZabQH5rQ7hOwZDEKYcxwnvoDyxw74wc4B6moDFMk+NzcwGaXIyHLUy0EFE659lZAKdJwL6QAVWkjIc0oDGM7irrpLI+UpoosP7BnG/V9wEzTvcYrOOKMrbaIV6MLwc9DrGYI80YwkcWZ9pKJiELMD9S0WrnjOEIcrJqTuTsFJjjm+agIwXeVSnQGPgjIPFGo2EdEo7nCcxXvBbScwTQpo6cQ8ZLdy513NW1w7RaxMM1eoJNV/AnmvKA4fnXRw27UrPRNiICGCG/kxNwvhYIPr7uDBC9BTmuXzIuesEXwX2Hn8Id2C2iPWrBfeT1tnmOblkyuqr2VSXDdj6AeAOxylGNfWQ/wAzeEP5D9SWff8pn4X9FabapvZpty0IskPhRL/ysF/JGXlWUzRyr6FManjAjAkz5KeeNzugCXkcXMK/qA572FT5kMUGUxf+vnUPLAK6+c3QIeaTQJx+ERqXA8LXszHg3ZHuBuuUZOmSgBTKvt3xJg2yeJZitSEqZf2l/O0zqQUTNPR+8tgu6p10QpklxkLIgG4lPHcGEkpjnc3ImA2cmjwMiEsQywBEo5eENWeYnKpMFjXl5UGBV9hlZBmLmEXkV/+GvjGG5ogDHmEEx+/u5JBfhl08YRjwcpqvgwsTVqAHu0LR0coDdEzlmhwDnugIxdpruXE6wBtq4xZ3HfcmDqeuDhlgZgseU4w7h94Fo24fs7YtoRXgeRwwNpy+/LLzgqGN6l5Rgs4Nm3ArGB1Rb3wUvy4Aecj1T9oZZeutEWHvxyJWJKD3/NjNQs2v+cWiTuEz+Ac5yDNBjUnpGSspY+3RKeFVGjNOM1PDRAsziMqh2dQFcMAZ+ceVR3JkZbaGmFByCYPS7esElQfvXIAhhZ7FouT4JUGINUUQzv/f7D1dLA1xNyEyjLQ8Y90SZ3SFwKkLN/oJJb0/cXT2YNNuuhUagL3nXT8Q/4yWCKMSiv2/YJ/qGe42UsyCv86YJdFgvmnPaHa7bLph0LlMEJZ9TyetLzurLE6N+hrhKN/+rGLyLtBotTtsdqPVuVkSx+lVB/q722wxw9y8=</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View File

@ -12,21 +12,23 @@ Ce sera une bonne indication à prendre en considération pour nos dépendances,
NOTE: en relisant, le passage ci-dessus n'est peut-être pas hyper clair... A refaire.
Dans cette partie, on va parler de *méthode de travail*, avec comme objectif visé, d'éviter que l'application ne tourne que sur notre machine et que chaque déploiement ne soit une plaie à gérer. Chaque mise à jour doit se limiter à:
Dans cette partie, on va parler de *méthode de travail*, avec comme objectif visé, d'éviter que l'application ne tourne que sur notre machine et que chaque déploiement ne soit une plaie à gérer. Chaque mise à jour doit se limiter à:
. démarrer un script,
. démarrer un script,
. prévoir un rollback si cela plante
. se préparer une tisane en regardant nos flux RSS (si cette technologie existe encore...).
**Remarque** : les commandes qui seront exécutés dans ce livre le seront depuis un shell sous GNU/Linux. Certaines devront donc être adaptées si vous êtes dans un autre environnement.
include::12-factors.adoc[]
== Construire des applications maintenables
include::maintainable-applications.adoc[]
include::maintainable-applications/12-factors.adoc[]
include::solid.adoc[]
include::maintainable-applications/maintainable-applications.adoc[]
include::environment.adoc[]
include::maintainable-applications/solid.adoc[]
include::environment/index.adoc[]
include::venvs.adoc[]

View File

@ -8,21 +8,37 @@ Concrètement, on pourrait tout à fait se limiter à Notepad ou Notepad++. C'es
* https://www.jetbrains.com/pycharm/[PyCharm]
* https://www.vim.org/[Vim] avec les plugins https://github.com/davidhalter/jedi-vim[Jedi-Vim], https://github.com/preservim/nerdtree[nerdtree]
=== Un terminal
image::images/environment/codium.png[]
=== Un terminal
Si vous êtes sous Windows, https://cmder.net/[cmder].
Pour tout autre système, vous devriez disposer en natif de ce qu'il faut.
image::images/environment/terminal.png[align="center"]
=== Un gestionnaire de base de données
PHPMyAdmin ou PgAdmin. _A priori_, les deux sont disponibles pour tous les systèmes d'exploitation, et sont nativement supportés par Django. Pour SQLite, il existe https://sqlitebrowser.org/[SQLiteBrowser] qui fait très bien le boulot. On abordera les <<Bases de données,systèmes de gestion de base de données>>.
Django gère plusieurs types de base de données.
Essayez de conformer votre environnement de développement à l'environnement sur lequel l'application sera sensée tourner en production:
* Pour *PostgreSQL*, il existe https://www.pgadmin.org/[pgAdmin]
* Pour *MariaDB* ou *MySQL*, partez sur https://www.phpmyadmin.net/[PHPMyAdmin]
* Pour *SQLite*, il existe https://sqlitebrowser.org/[SQLiteBrowser]
PHPMyAdmin ou PgAdmin.
_A priori_, ces trois types de bases de données sont disponibles pour tous les systèmes d'exploitation, et sont nativement supportés par Django.
Pour tous les autres cas, le shell Django pourra largement suffire.
=== Un gestionnaire de mots de passe
https://keepassxc.org/[KeepassXC]. On en aura besoin pour gé(né)rer des phrases secrètes pour nos applications.
Nous en auront besoin pour gé(né)rer des phrases secrètes pour nos applications.
Si vous n'en utilisez pas déjà un, partez sur https://keepassxc.org/[KeepassXC].
=== Un système de gestion de versions
image::images/environment/keepass.png[align="center"]
=== Un système de gestion de versions
https://git-scm.com/[Git], pour développer rapidement des preuves de concept, switcher vers une nouvelle fonctionnalité à développer, un bogue à réparer ou une nouvelle release à proposer au téléchargement. Même en développant seul dans son coin, un système de gestion de versions reste indispensable.

View File

@ -1,6 +1,6 @@
== 12 facteurs
=== 12 facteurs
Pour la méthode de travail et de développement, on va se baser sur les https://12factor.net/fr/[The Twelve-factor App] - ou plus simplement les *12 facteurs*.
Pour la méthode de travail et de développement, on va se baser sur les https://12factor.net/fr/[The Twelve-factor App] - ou plus simplement les *12 facteurs*.
L'idée derrière cette méthode consiste à pousser les concepts suivants (repris grossièrement de la https://12factor.net/fr/[page d'introduction] :
@ -9,9 +9,9 @@ L'idée derrière cette méthode consiste à pousser les concepts suivants (repr
. *Minimiser les divergences entre les différents environnemens composant un projet*
. *Augmenter l'agilité générale du projet*, en permettant une meilleure évolutivité architecturale et une meilleure mise à l'échelle - _Vous avez 5000 utilisateurs en plus? Ajoutez un serveur et on n'en parle plus ;-)_.
En pratique, les idées qui se trouvent derrière les points ci-dessus permettront de monter facilement un nouvel environnement - qu'il soit sur la machine du petit nouveau dans l'équipe, sur un serveur Azure/Heroku/Digital Ocean ou votre nouveau Raspberry Pi Zéro caché à la cave.
En pratique, les idées qui se trouvent derrière les points ci-dessus permettront de monter facilement un nouvel environnement - qu'il soit sur la machine du petit nouveau dans l'équipe, sur un serveur Azure/Heroku/Digital Ocean ou votre nouveau Raspberry Pi Zéro caché à la cave.
Pour reprendre de manière très brute les différentes idées derrière cette méthode, on a:
Pour reprendre de manière très brute les différentes idées derrière cette méthode, on a:
. *Une base de code unique, suivie par un système de contrôle de versions*. Chaque déploiement de l'application se basera sur cette source, afin de minimiser les différences que l'on pourrait trouver entre deux environnements d'un même projet. On utilisera un dépôt Git - Github, Gitlab, Gitea, ... Au choix.
@ -19,7 +19,7 @@ Pour reprendre de manière très brute les différentes idées derrière cette m
. *Sauver la configuration directement au niveau de l'environnement*. On veut par exemple éviter d'avoir à recompiler/redéployer l'application parce que l'adresse du serveur de messagerie a été modifiée, ou parce qu'un protocole a changé en cours de route. Concrètement, toute information susceptible de modifier le comportement intrinsèque de l'application doit se trouver dans un fichier ou dans une variable d'environnement. En allant un pas plus loin, cela permettra de paramétrer facilement un container, simplement en modifiant une variable de configuration, qui spécifiera la base de données sur laquelle l'application devra se connecter. Toute clé de configuration (nom du serveur de base de données, adresse d'un service Web externe, clé d'API pour l'interrogation d'une ressource, ...) sera définie directement au niveau de l'hôte - à aucun moment, on ne doit trouver un mot de passe en clair dans le dépôt source ou une valeur susceptible d'évoluer, écrite en dur dans le code.
. *Traiter les ressources externes comme des ressources attachées*. On parle de bases de données, de services de mise en cache, d'API externes, ... L'application doit également être capable d'effectuer des changements au niveau de ces ressources sans que le code intrinsèque ne soit modifié. On parle alors de *ressources attachées*, dont la présence est nécessaire au bon fonctionnement de l'application, mais pour lesquelles le *type* n'est pas obligatoirement défini. On veut par exemple "une base de données" et "une mémoire cache", et pas "une base MariaDB et une instance Memcached". De cette manière, les ressources peuvent être attachées et détachées d'un déploiement à la volée. Si une base de données ne fonctionne pas correctement (problème matériel?), l'administrateur pourrait simplement restaurer un nouveau serveur à partir d'une précédente sauvegarde, et l'attacher à l'application sans que le code source ne soit modifié. une solution consiste à passer toutes ces informations (nom du serveur et type de base de données, clé d'authentification, ...) directement via des variables d'environnement.
. *Traiter les ressources externes comme des ressources attachées*. On parle de bases de données, de services de mise en cache, d'API externes, ... L'application doit également être capable d'effectuer des changements au niveau de ces ressources sans que le code intrinsèque ne soit modifié. On parle alors de *ressources attachées*, dont la présence est nécessaire au bon fonctionnement de l'application, mais pour lesquelles le *type* n'est pas obligatoirement défini. On veut par exemple "une base de données" et "une mémoire cache", et pas "une base MariaDB et une instance Memcached". De cette manière, les ressources peuvent être attachées et détachées d'un déploiement à la volée. Si une base de données ne fonctionne pas correctement (problème matériel?), l'administrateur pourrait simplement restaurer un nouveau serveur à partir d'une précédente sauvegarde, et l'attacher à l'application sans que le code source ne soit modifié. une solution consiste à passer toutes ces informations (nom du serveur et type de base de données, clé d'authentification, ...) directement via des variables d'environnement.
. *Séparer proprement les phases de construction, de mise à disposition et d'exécution*. La *construction* (_build_) convertit un code source en un ensemble de fichiers exécutables, associé à une version et à une transaction dans le système de gestion de sources. La *mise à disposition* (_release_) associe cet ensemble à une configuration prête à être exécutée, tandis que la phase d'*exécution* (_run_) démarre les processus nécessaires au bon fonctionnement de l'application. On doit pouvoir se baser sur les _releases_ de Gitea, sur un serveur d'artefacts ou sur https://fr.wikipedia.org/wiki/Capistrano_(logiciel)[Capistrano].
@ -31,7 +31,7 @@ Pour reprendre de manière très brute les différentes idées derrière cette m
. *Améliorer la robustesse de l'application grâce à des arrêts élégants et à des démarrages rapides*. Par "arrêt élégant", on veut surtout éviter le `kill -9 <pid>` ou tout autre rechargement brutal du superviseur; de cette manière, on évite qu'une requête d'un utilisateur n'aboutisse sur un code d'erreur. Au contraire, les requêtes en cours doivent être terminées au mieux, tandis que le démarrage rapide de nouveaux processus limite les perturbations et améliore la balance du travail d'un processus en cours d'extinction vers des processus tout frais. L'intégration de ces mécanismes dès les premières étapes de développement limite les perturbations et facilite la prise en compte d'arrêts inopinés (problème matériel, redémarrage du système hôte, etc.).
. *Conserver les différents environnements aussi similaires que possible, et limiter les divergences entre un environnement de développement et de production*. L'exemple donné est un développeur qui utilise macOS, NGinx et SQLite, tandis que l'environnement de production tourne sur une CentOS avec Apache2 et PostgreSQL. L'idée derrière ce concept limite les divergences entre environnements, facilite les déploiements et limite la casse et la découverte de modules non compatibles dès les premières phases de développement.
. *Conserver les différents environnements aussi similaires que possible, et limiter les divergences entre un environnement de développement et de production*. L'exemple donné est un développeur qui utilise macOS, NGinx et SQLite, tandis que l'environnement de production tourne sur une CentOS avec Apache2 et PostgreSQL. L'idée derrière ce concept limite les divergences entre environnements, facilite les déploiements et limite la casse et la découverte de modules non compatibles dès les premières phases de développement.
. *Gérer les journeaux d'évènements comme des flux*. Une application ne doit jamais se soucier de l'endroit où ces évènements seront écrits, mais simplement de les envoyer sur la sortie `stdout`. De cette manière, qu'on soit en développement sur le poste d'un développeur avec une sortie console ou sur une machine de production avec un envoi vers une instance https://www.graylog.org/[Greylog], le routage des journaux sera réalisé en fonction de sa nécessité et de sa criticité.

View File

@ -1,4 +1,4 @@
== Construire des applications maintenables
=== Construire des applications maintenables
Pour cette section, je me base d'un résumé de l'ebook **Building Maintenable Software** disponible chez link:++O'Reilly++[http://shop.oreilly.com/product/0636920049555.do].
@ -9,29 +9,27 @@ Ce livre répartit un ensemble de conseils parmi quatre niveaux de composants:
* Les composants
* Et des conseils plus généraux.
=== Au niveau des méthodes et fonctions
==== Au niveau des méthodes et fonctions
* *Gardez vos méthodes/fonctions courtes*. Pas plus de 15 lignes, en comptant les commentaires. Des exceptions sont possibles, mais dans une certaine mesure uniquement (pas plus de 6.9% de plus de 60 lignes; pas plus de 22.3% de plus de 30 lignes, au plus 43.7% de plus de 15 lignes et au moins 56.3% en dessous de 15 lignes). Oui, c'est dur à tenir, mais faisable.
* *Conserver une complexité de McCabe en dessous de 5*, c'est-à-dire avec quatre branches au maximum. A nouveau, si on a une méthode avec une complexité cyclomatique de 15, la séparer en 3 fonctions avec une complexité de 5 conservera globalement le nombre 15, mais rendra le code de chacune de ces méthodes plus lisible, plus maintenable.
* *N'écrivez votre code qu'une seule fois: évitez les duplications, copie, etc.*, c'est juste mal: imaginez qu'un bug soit découvert dans une fonction; il devra alors être corrigé dans toutes les fonctions qui auront été copiées/collées. C'est aussi une forme de régression.
* *Conservez de petites interfaces*. Quatre paramètres, pas plus. Au besoin, refactorisez certains paramètres dans une classe, plus facile à tester.
=== Au niveau des classes
==== Au niveau des classes
* *Privilégiez un couplage faible entre vos classes*. Ceci n'est pas toujours possible, mais dans la mesure du possible, éclatez vos classes en fonction de leur domaine de compétences respectif. L'implémentation du service `UserNotificationsService` ne doit pas forcément se trouver embarqué dans une classe `UserService`. De même, pensez à passer par une interface (commune à plusieurs classes), afin d'ajouter une couche d'abstraction. La classe appellante n'aura alors que les méthodes offertes par l'interface comme points d'entrée.
=== Au niveau des composants
==== Au niveau des composants
* *Tout comme pour les classes, il faut conserver un couplage faible au niveau des composants* également. Une manière d'arriver à ce résultat est de conserver un nombre de points d'entrée restreint, et d'éviter qu'on ne puisse contacter trop facilement des couches séparées de l'architecture. Pour une architecture n-tiers par exemple, la couche d'abstraction à la base de données ne peut être connue que des services; sans cela, au bout de quelques semaines, n'importe quelle couche de présentation risque de contacter directement la base de données, "juste parce qu'elle en a la possibilité". Vous pourrez également passer par des interfaces, afin de réduire le nombre de points d'entrée connus par un composant externe (qui ne connaîtra par exemple que `IFileTransfer` avec ses méthodes `put` et `get`, et non pas les détails d'implémentation complet d'une classe `FtpFileTransfer` ou `SshFileTransfer`).
* *Conserver un bon balancement au niveau des composants*: évitez qu'un composant **A** ne soit un énorme mastodonte, alors que le composant juste à côté n'est capable que d'une action. De cette manière, les nouvelles fonctionnalités seront mieux réparties parmi les différents systèmes, et les responsabilités plus faciles à gérer. Un conseil est d'avoir un nombre de composants compris entre 6 et 12 (idéalement, 12), et que ces composants soient approximativement de même taille.
=== De manière plus générale
==== De manière plus générale
* *Conserver une densité de code faible*: il n'est évidemment pas possible d'implémenter n'importe quelle nouvelle fonctionnalité en moins de 20 lignes de code; l'idée ici est que la réécriture du projet ne prenne pas plus de 20 hommes/mois. Pour cela, il faut (activement) passer du temps à réduire la taille du code existant: soit en faisant du refactoring (intensif?), soit en utilisant des librairies existantes, soit en explosant un système existant en plusieurs sous-systèmes communiquant entre eux. Mais surtout en évitant de copier/coller bêtement du code existant.
* *Automatiser les tests*, *ajouter un environnement d'intégration continue dès le début du projet* et *vérifier par des outils les points ci-dessus*.
=== En pratique
==== En pratique
Par rapport aux points repris ci-dessus, l'environnement Python et le framework Django proposent un ensemble d'outils intégrés qui permettent de répondre à chaque point. Avant d'aller plus loin, donc, un petit point sur les conventions, les tests (unitaires, orientés comportement, basés sur la documentation, ...), la gestion de version du code, le typage de paramètre et de valeurs de retour et sur la documentation. Plus que dans tout langage compilé, ceux-ci sont pratiquement obligatoires. Vous pourrez les voir comme une perte de temps dans un premier temps, mais nous vous promettons qu'ils vous en feront gagner par la suite.
NOTE: parlez des principes SOLID

View File

@ -1,4 +1,4 @@
== SOLID
=== SOLID
. S : SRP (Single Responsibility
. O : Open closed
@ -6,25 +6,25 @@
. I : Interface Segregation
. D : Dependency Inversion
=== Single Responsibility Principle
==== Single Responsibility Principle
Le principe de responsabilité unique définit que chaque concept ou domaine d'activité ne s'occupe que d'une et d'une seule chose. En prenant l'exemple d'une méthode qui communique avec une base de données, ce ne sera pas à cette méthode à gérer l'inscription d'une exception à un emplacement quelconque. Cette action doit être prise en compte par une autre classe (ou un autre concept), qui s'occupera elle de définir l'emplacement où l'évènement sera enregistré (base de données, Graylog, fichier, ...).
Cette manière d'organiser le code ajoute une couche d'abstraction (ie. "I don't care") sur les concepts, et centralise tout ce qui touche à type d'évènement à un et un seul endroit. Ceci permet également de centraliser la configuration pour ce type d'évènements, et augmenter la testabilité du code.
=== Open Closed
==== Open Closed
Un des principes essentiels en programmation orientée objets est l'héritage de classes et la surcharge de méthodes: plutôt que de partir sur une série de comparaisons pour définir le comportement d'une instance, il est parfois préférable de définir une nouvelle sous-classe, qui surcharge une méthode bien précise. Pour l'exemple, on pourrait ainsi définir trois classes:
* Une classe `Customer`, pour laquelle la méthode `GetDiscount` ne renvoit rien;
* Une classe `SilverCustomer`, pour laquelle la méthode revoit une réduction de 10%;
* Une classe `SilverCustomer`, pour laquelle la méthode revoit une réduction de 10%;
* Une classe `GoldCustomer`, pour laquelle la même méthode renvoit une réduction de 20%.
Si on rencontre un nouveau type de client, il suffit alors de créer une nouvelle sous-classe. Cela évite d'avoir à gérer un ensemble conséquent de conditions dans la méthode initiale, en fonction d'une autre variable (ici, le type de client).
En anglais, dans le texte : "Putting in simple words the “Customer” class is now closed for any new modification but its open for extensions when new customer types are added to the project.". En résumé: on ferme la classe `Customer` à toute modification, mais on ouvre la possibilité de créer de nouvelles extensions en ajoutant de nouveaux types [héritant de `Customer`].
=== Liskov Substitution
==== Liskov Substitution
Le principe de substitution fait qu'une classe B qui hérite d'une classe A doit se comporter de la même manière que cette dernière. Il n'est pas question que la classe B n'implémente pas certaines méthodes, alors que celles-ci sont disponibles pour A.
@ -34,7 +34,7 @@ Le principe de substitution fait qu'une classe B qui hérite d'une classe A doit
Ce principe s'applique à tout type de polymorphisme, et même aux langages de type *duck typing*: "when I see a bird that quacks like a duck, walks like a duck, has feathers and webbed feet and associates with ducks—Im certainly going to assume that he is a duck" (Source: http://en.wikipedia.org/wiki/Duck_test[Wikipedia (as usual)]). Pour le cas émis ci-dessus, ce n'est donc pas parce qu'une classe a besoin **d'une méthode** définie dans une autre classe qu'elle doit forcément en hériter. Cela bousillerait le principe de substitution (et par la même occasion le *duck test*).
=== Interface Segregation
==== Interface Segregation
Ce principe stipule qu'un client ne peut en aucun cas dépendre d'une méthode dont il n'a pas besoin. Plus simplement, plutôt que de dépendre d'une seule et même (grosse) interface présentant un ensemble conséquent de méthodes, il est proposé d'exploser cette interface en plusieurs (plus petites) interfaces. Ceci permet aux différents clients de n'utiliser qu'un sous-ensemble précis d'interfaces, répondant chacune à un besoin particulier.
@ -45,7 +45,7 @@ Pour contrer ceci, on aurait alors une première interface permettant la lecture
* A : lecture
* B (héritant de A) : lecture (par A) et écriture.
=== Dependency inversion
==== Dependency inversion
Dans une architecture conventionnelle, les composants de haut-niveau dépendant directement des composants de bas-niveau. Une manière très simple d'implémenter ceci est d'instancier un nouveau composant. L'inversion de dépendances stipule que c'est le composant de haut-niveau qui possède la définition de l'interface dont il a besoin, et le composant de bas-niveau qui l'implémente.
@ -58,7 +58,7 @@ Le composant de haut-niveau peut donc définir qu'il s'attend à avoir un `Publi
L'injection de dépendances est un patron de programmation qui suit le principe d'inversion de dépendances.
=== Sources
==== Sources
* http://www.codeproject.com/Articles/703634/SOLID-architecture-principles-using-simple-Csharp[Understanding SOLID principles on CodeProject]
* http://en.wikipedia.org/wiki/Software_craftsmanship[Software Craftmanship]