Préface
Dans ce chapitre, nous allons expliquer le cinquième chapitre de la mise en œuvre des cinq principaux principes du langage JavaScript solide, qui est le LSP (le principe de l'inversion de dépendance).
Texte anglais original: http://freshbrewedcode.com/derekgreeer/2012/01/22/solid-javascript-the-dependency-inversion-principle/
Principe d'inversion de recours
La description du principe de l'inversion de la dépendance est:
A. Les modules de haut niveau ne devraient pas dépendre de modules de bas niveau. Les deux devraient dépendre d'abstractions.
Les modules de haut niveau ne devraient pas dépendre de modules de bas niveau, les deux devraient s'appuyer sur l'abstraction
B. Les abstractions ne devraient pas dépendre des détails. Les détails devraient dépendre des abstractions.
Le résumé ne doit pas dépendre des détails, les détails devraient dépendre de l'abstraction
Le problème le plus important avec le principe de l'inversion de dépendance est de s'assurer que les principales composantes de l'application ou du cadre sont découplées des détails de mise en œuvre de la composante sous-jacente non importante, ce qui garantira que les parties les plus importantes du programme ne sont pas affectées par les modifications et les modifications des composants de bas niveau.
La première partie de ce principe concerne le couplage entre les modules de haut niveau et les modules de bas niveau. Dans l'architecture de division traditionnelle, les modules de haut niveau (encapsuler la logique métier principale du programme) s'appuient toujours sur certains modules de bas niveau (certains points de base). Lorsque le principe d'inversion de dépendance est appliqué, la relation est inversée. Contrairement aux modules de haut niveau qui reposent sur des modules de bas niveau, l'inversion de dépendance fait que les modules de bas niveau reposent sur des interfaces définies dans les modules de haut niveau. Par exemple, si vous souhaitez persister des données pour un programme, la conception traditionnelle est que le module de base dépend de l'API d'un module persistant. Après la reconstruction selon le principe de l'inversion de dépendance, le module de base doit définir une interface API persistante, puis l'instance d'implémentation persistante doit implémenter l'interface API définie par le module de base.
La deuxième partie de ce principe décrit la relation correcte entre l'abstraction et les détails. Comprendre cette partie est plus utile en comprenant le langage C ++ car son applicabilité est plus évidente.
Contrairement à certaines langues typées statiquement, C ++ ne fournit pas de concept de niveau de langue pour définir les interfaces. Quelle est la relation entre la définition de classe et la mise en œuvre de la classe? En C ++, les classes sont définies sous la forme de fichiers d'en-tête, qui définissent les méthodes et variables des membres de la classe que le fichier source doit implémenter. Étant donné que toutes les variables et méthodes privées sont définies dans le fichier d'en-tête, elles peuvent être utilisées pour les abstraire et les découpler avant les détails de la mise en œuvre. Le concept d'interface est implémenté en définissant uniquement des méthodes abstraites (la classe de base abstraite en C ++) est utilisée pour implémenter des classes.
Plongeon et javascript
Parce que JavaScript est un langage dynamique, il n'est pas nécessaire de résumer pour le découplage. Par conséquent, l'abstraction ne devrait pas s'appuyer sur les détails. Ce changement n'a pas beaucoup d'impact sur JavaScript, mais les modules de haut niveau ne devraient pas s'appuyer sur des modules de bas niveau mais ont un grand impact.
Lorsque vous discutez du principe de l'inversion de dépendance dans le contexte des langues typées statiquement, les concepts de couplage incluent sémantique et physique. Cela signifie que si un module de haut niveau dépend d'un module de bas niveau, il couple non seulement l'interface sémantique, mais également l'interface physique définie dans le module sous-jacent. En d'autres termes, les modules de haut niveau doivent non seulement être découplés à partir de la bibliothèque tierce, mais également à partir de modules de bas niveau natifs.
Pour expliquer cela, imaginez qu'un programme .NET pourrait contenir un module de haut niveau très utile qui s'appuie sur un module persistant de bas niveau. Lorsque l'auteur doit ajouter une interface similaire à l'API de persistance, que le principe d'inversion de dépendance soit utilisé ou non, les modules de haut niveau ne peuvent pas être réutilisés dans d'autres programmes avant de réimplémenter la nouvelle interface de ce module de bas niveau.
En JavaScript, l'applicabilité du principe d'inversion de dépendance est limitée au couplage sémantique entre les modules de haut niveau et les modules de bas niveau. Par exemple, la DIP peut ajouter des interfaces au besoin au lieu de coupler les interfaces implicites définies par des modules de bas niveau.
Pour comprendre cela, jetons un coup d'œil à l'exemple suivant:
La copie de code est la suivante:
$ .fn.trackmap = fonction (options) {
var defaults = {
/ * par défaut * /
};
options = $ .extend ({}, par défaut, options);
var mapoptions = {
Centre: nouveau google.maps.latlng (options.latitude, options.longitude),
Zoom: 12,
MapTypeid: google.maps.maptypeid.roadmap
},
map = new Google.maps.map (this [0], mapoptions),
pos = new Google.maps.latlng (options.Latitude, options.longitude);
var marker = new Google.maps.marker ({{{{
Position: pos,
Titre: Options.Title,
icône: options.icon
});
marker.setmap (map);
options.feed.update (fonction (latitude, longitude) {
Marker.SetMap (null);
var newlatlng = new Google.maps.latlng (latitude, longitude);
Marker.Position = newlatlng;
marker.setmap (map);
map.setCenter (newlatlng);
});
retourner ceci;
};
var updater = (function () {
// Propriétés privées
retour {
Mise à jour: fonction (rappel) {
UpdateMap = rappel;
}
};
}) ();
$ ("# map_canvas"). trackmap ({{
Latitude: 35.044640193770725,
Longitude: -89.98193264007568,
icône: «http://bit.ly/zjngde»,
Titre: «Numéro de suivi: 12345»,
Feed: Updater
});
Dans le code ci-dessus, il existe une petite bibliothèque de classe JS qui convertit une div en carte pour afficher les informations de localisation actuellement suivie. La fonction TrackMap a 2 dépendances: l'API tiers Google Maps et le flux de localisation. La responsabilité de l'objet Feed est d'appeler un rappel de rappel (fourni à l'initialisation) lorsque la position de l'icône est mise à jour et passer dans la latitude de latitude et la longitude de précision. L'API Google Maps est utilisée pour rendre les interfaces.
L'interface de l'objet de flux peut être basée sur l'installation ou non conçue en fonction des exigences de la fonction de trackmap d'installation. En fait, son rôle est très simple, se concentrant sur des implémentations simples simples et n'a pas besoin de s'appuyer autant sur Google Maps. La sémantique TrackMap est couplée à l'API Google Maps. Si vous devez passer à différents fournisseurs de cartes, vous devez réécrire la fonction TrackMap afin qu'il puisse s'adapter à différents fournisseurs.
Afin de retourner le couplage sémantique de la bibliothèque de classe Google Maps, nous devons réécrire la fonction de conception TrackMap pour coupler sémantique une interface implicite (abstraction de l'interface du fournisseur de cartes fournisseur). Nous avons également besoin d'un objet d'implémentation adapté à l'API Google Maps. Ce qui suit est la fonction TrackMap refactorisée:
La copie de code est la suivante:
$ .fn.trackmap = fonction (options) {
var defaults = {
/ * par défaut * /
};
options = $ .extend ({}, par défaut, options);
options.provider.showmap (
ce [0],
options.latitude,
options.longitude,
options.Icon,
options.title);
options.feed.update (fonction (latitude, longitude) {
options.provider.updatemap (latitude, longitude);
});
retourner ceci;
};
$ ("# map_canvas"). trackmap ({{
Latitude: 35.044640193770725,
Longitude: -89.98193264007568,
icône: «http://bit.ly/zjngde»,
Titre: «Numéro de suivi: 12345»,
Feed: Updater,
fournisseur: trackmap.googlemapsprovider
});
Dans cette version, nous avons repensé la fonction TrackMap et l'interface du fournisseur de carte requise, puis déplacés les détails d'implémentation dans un composant GoogleMapsProvider séparé, qui peut être encapsulé indépendamment dans un module JavaScript séparé. Voici mon implémentation GoogleMapsProvider:
La copie de code est la suivante:
trackmap.googlemapsprovider = (function () {
marqueur var, carte;
retour {
showmap: fonction (élément, latitude, longitude, icône, titre) {
var mapoptions = {
Centre: nouveau google.maps.latlng (latitude, longitude),
Zoom: 12,
MapTypeid: google.maps.maptypeid.roadmap
},
pos = new Google.maps.latlng (latitude, longitude);
map = new Google.maps.map (élément, mapoptions);
Marker = new Google.maps.marker ({{{{
Position: pos,
Titre: Titre,
icône: icône
});
marker.setmap (map);
},
UpdateMap: fonction (latitude, longitude) {
Marker.SetMap (null);
var newlatlng = new Google.maps.latlng (latitude, longitude);
Marker.Position = newlatlng;
marker.setmap (map);
map.setCenter (newlatlng);
}
};
}) ();
Après avoir apporté les modifications ci-dessus, la fonction TrackMap deviendra très flexible et n'a pas à compter sur l'API Google Maps. Au lieu de cela, d'autres fournisseurs de cartes peuvent être remplacés à volonté, c'est-à-dire que tout fournisseur de carte peut être adapté en fonction des besoins du programme.
Quand l'injection de dépendance est-elle?
C'est un peu sans rapport. En fait, le concept d'injection de dépendance est souvent mélangé avec le principe de l'inversion de dépendance. Afin de clarifier cette différence, il est nécessaire d'expliquer:
L'injection de dépendance est une forme spéciale d'inversion de contrôle, et l'inversion signifie comment un composant acquiert ses dépendances. L'injection de dépendance signifie: la dépendance est fournie au composant, plutôt qu'au composant pour obtenir la dépendance, ce qui signifie créer une instance de la dépendance, demander la dépendance via l'usine et demander la dépendance via le localisateur de service ou le composant lui-même. Le principe d'inversion de dépendance et l'injection de dépendance sont à la fois axés sur les dépendances et sont utilisés pour l'inversion. Cependant, le principe de l'inversion de dépendance ne se concentre pas sur la façon dont les composants acquièrent des dépendances, mais uniquement sur la façon dont les modules de niveau élevé sont découplés à partir de modules de bas niveau. Dans un sens, le principe de l'inversion de dépendance est une autre forme d'inversion de contrôle. Ici, l'inversion est quel module définit l'interface (définie du niveau inférieur, l'inversion au niveau supérieur).
Résumer
Il s'agit du dernier article des cinq principes majeurs. Dans ces 5 articles, nous voyons à quel point Solid est mis en œuvre en JavaScript. Différents principes sont expliqués sous différents angles en JavaScript. (Note de l'oncle: en fait, je pense que bien qu'il soit un peu inapproprié, sous une autre perspective, les principes généraux sont en fait les mêmes dans diverses langues.)