This article mainly introduces the JavaScript series (22): S.O.L. i. d. Detailed description of the dependency inversion principle DIP of the five principles. This article describes the content of DIPandJavaScript and when dependency injection. For more information, see.
Preface
In this chapter, we will explain 5th of The Five Principles of S.O.L. I. D implemented by The JavaScript language and The Dependency Inversion Principle LSP (The Dependency Inversion Principle ).
Http://freshbrewedcode.com/derekgreer/2012/01/22/solid-javascript-the-dependency-inversion-principle/.
Dependency inversion principle
The dependency inversion principle is described as follows:
A. High-level modules shocould not depend on low-level modules. Both shold depend on each actions.
High-level modules should not depend on low-level modules, both of which should depend on abstraction.
B. Define actions shocould not depend upon details. Details shocould depend upon login actions.
Abstraction should not depend on details, and details should depend on abstraction
The most important issue of the dependency inversion principle is to ensure that the main components of an application or framework are decoupled from the Implementation Details of non-important underlying components, this will ensure that the most important part of the program will not be affected by the changes to low-level components.
The first part of this principle is about the coupling between the high-level module and the low-level module. In the traditional divided architecture, the high-level module encapsulates the core business logic of the program) it depends on some lower-layer modules (some basic points ). When the Dependency inversion principle is applied, the relationship is reversed. Unlike the higher-level modules, the dependency inversion enables the lower-level modules to depend on the interfaces defined in the higher-level modules. For example, if data persistence is required for a program, the traditional design is that the core module depends on the API of a persistence module. After Reconstruction Based on the Dependency inversion principle, the core module needs to define the persistent API interface, and then the persistent implementation instance needs to implement the API interface defined by the core module.
The second part of this principle describes the correct relationship between abstraction and details. Understanding this part is helpful by understanding the C ++ language, because it has obvious applicability.
Unlike some static languages, C ++ does not provide a language-level concept to define interfaces. What is the difference between class definition and class implementation? In C ++, classes are defined in the form of header files, which define the class member methods and variables to be implemented by the source files. Because all the variables and private methods are defined in the header file, they can be used to abstract and decouple from the implementation details. You can define only abstract methods to implement (C ++ is an abstract base class) interfaces for implementing classes.
DIP and JavaScript
JavaScript is a dynamic language, so it does not need to be abstracted for decoupling. Therefore, abstraction should not depend on the details. This change does not have much impact on JavaScript, but the high-level module should not depend on the lower-level module, but it has a great impact.
When the Dependency inversion principle is discussed in the context of static language, the concept of coupling includes two types: semantic and physical. That is to say, if a high-level module depends on a low-level module, that is, it is coupled with both semantic interfaces and physical interfaces defined in the underlying module. That is to say, the high-level module should not only be decoupled from the third-party class library, but also from the native low-level module.
To explain this, imagine a. NET program may contain a very useful high-level module, which relies on a low-level persistence module. When the author needs to add a similar interface to the persistent API, whether or not the Dependency inversion principle is used, the high-level module cannot be reused in other programs before it implements the new interface of this low-level module.
In JavaScript, the applicability of the dependency inversion principle is limited to semantic coupling between high-level modules and lower-level modules. For example, DIP can add interfaces as needed rather than coupling the implicit interfaces defined by lower-level modules.
To understand this, let's take a look at the following example:
The Code is as follows:
$. Fn. trackMap = function (options ){
Var defaults = {
/* Defaults */
};
Options = $. extend ({}, defaults, options );
Var mapOptions = {
Center: new google. maps. LatLng (options. latitude, options. longpolling ),
Zoom: 12,
MapTypeId: google. maps. MapTypeId. ROADMAP
},
Map = new google. maps. Map (this [0], mapOptions ),
Pos = new google. maps. LatLng (options. latitude, options. longpolling );
Var marker = new google. maps. Marker ({
Position: pos,
Title: options. title,
Icon: options. icon
});
Marker. setMap (map );
Options. feed. update (function (latitude, longpolling ){
Marker. setMap (null );
Var newLatLng = new google. maps. LatLng (latitude, longpolling );
Marker. position = newLatLng;
Marker. setMap (map );
Map. setCenter (newLatLng );
});
Return this;
};
Var updater = (function (){
// Private properties
Return {
Update: function (callback ){
UpdateMap = callback;
}
};
})();
$ ("# Map_canvas"). trackMap ({
Latitudes: 35.044640193770725,
Longpolling:-89.98193264007568,
Icon: 'http: // bit. ly/zjngde ',
Title: 'tracking Number: 100 ',
Feed: updater
});
In the above Code, a small JS class library converts a DIV into a Map to display the position information of the current trail. The trackMap function has two dependencies: a third-party Google Maps API and Location feed. This feed object is responsible for calling a callback (provided during initialization) when the icon position is updated and passing in latitude and precision longpolling. Google Maps APIs are used to render interfaces.
The interface of the feed object may be designed according to the loading or the requirements of the trackMap function. In fact, his role is very simple and focuses on different simple implementations, it does not need to be so dependent on Google Maps. Google Maps API is coupled in trackMap semantics. If you need to switch to different map providers, You have to rewrite the trackMap function to adapt to different providers.
To flip the semantic coupling of the Google maps class library, we need to rewrite the trackMap function to perform semantic coupling on an implicit interface (abstracted from the interface of the map provider, we also need an implementation object adapted to the Google Maps API. The following is the reconstructed trackMap function:
The Code is as follows:
$. Fn. trackMap = function (options ){
Var defaults = {
/* Defaults */
};
Options = $. extend ({}, defaults, options );
Options. provider. showMap (
This [0],
Options. latitude,
Options. longpolling,
Options. icon,
Options. title );
Options. feed. update (function (latitude, longpolling ){
Options. provider. updateMap (latitude, longpolling );
});
Return this;
};
$ ("# Map_canvas"). trackMap ({
Latitudes: 35.044640193770725,
Longpolling:-89.98193264007568,
Icon: 'http: // bit. ly/zjngde ',
Title: 'tracking Number: 100 ',
Feed: updater,
Provider: trackMap. googleMapsProvider
});
In this version, we re-designed the trackMap function and a required map provider interface, and moved the implementation details to a separate googleMapsProvider component, this component may be encapsulated into a separate JavaScript module. The following is my googleMapsProvider implementation:
The Code is as follows:
TrackMap. googleMapsProvider = (function (){
Var marker, map;
Return {
ShowMap: function (element, latitude, longpolling, icon, title ){
Var mapOptions = {
Center: new google. maps. LatLng (latitude, longpolling ),
Zoom: 12,
MapTypeId: google. maps. MapTypeId. ROADMAP
},
Pos = new google. maps. LatLng (latitude, longpolling );
Map = new google. maps. Map (element, mapOptions );
Marker = new google. maps. Marker ({
Position: pos,
Title: title,
Icon: icon
});
Marker. setMap (map );
},
UpdateMap: function (latitude, longpolling ){
Marker. setMap (null );
Var newLatLng = new google. maps. LatLng (latitude, longpolling );
Marker. position = newLatLng;
Marker. setMap (map );
Map. setCenter (newLatLng );
}
};
})();
After the above changes, the trackMap function will become very flexible and does not have to rely on Google Maps API. On the contrary, other map providers can be replaced at will, that is to say, you can adapt to any map provider as needed.
When is dependency injection?
In fact, the concept of dependency injection is often mixed with the Dependency inversion principle. To clarify this difference, we need to explain:
Dependency injection is a special form of inversion control. inversion means how a component gets its dependencies. Dependency injection means that the dependency is provided to the component, rather than the component to obtain the dependency. It means to create a dependent instance and request the dependency through the factory, request this dependency through Service Locator or component initialization. The dependency inversion principle and dependency injection both focus on dependencies and are used for inversion. However, the dependency inversion principle does not focus on how components obtain dependencies, but on how high-level modules can be decoupled from low-level modules. In a sense, the dependency inversion principle is another form of control inversion, where the module definition interface is reversed (from the lower layer to the higher layer ).
Summary
This is the last article of the five principles. In these five texts, we can see how SOLID is implemented in JavaScript, different principles are illustrated in JavaScript from different perspectives. (Uncle note: in fact, he thinks that although it is a bit nondescribable, the general principle is the same in different languages .)