Dependence Injection)

Source: Internet
Author: User

As mentioned above: dependency injection is a classic article by Ma. In fact, this article is equivalent to an explanation of that article. Therefore, if you have a deep understanding of the original text, you do not need to read this article at all; however, if you have read it before like the author, it seems that you have understood it, however, it seems that I have not caught any points. It may be helpful for you to understand the original article.

1. Where is the dependency?
Here is a small example of a movie enumerator (movielist). The movie enumerator must use the services provided by a movie finder. The pseudo code is as follows:

1/* Service Interface */
2 public interface moviefinder {
3 arraylist findall ();
4}
5
6/* service consumer */
7 class movielister
8 {
9 Public movie [] moviesdirectedby (string Arg ){
10 list allmovies = finder. findall ();
11 For (iterator it = allmovies. iterator (); it. hasnext ();){
12 movie = (movie) it. Next ();
13 if (! Movie. getdirector (). Equals (ARG) it. Remove ();
14}
15 return (movie []) allmovies. toarray (new movie [allmovies. Size ()]);
16}
17
18/* the consumer contains an object that will point to a specific service type */
19 private moviefinder finder;
20/* the consumer needs to instantiate a specific service at a certain time point. This is the key to decoupling,
21 * This processing method leads to strong coupling between service consumers and service providers (this coupling was determined during compilation ).
22 **/
23 public movielister (){
24 finder = new colondelimitedmoviefinder ("movies1.txt ");
25}
26}


From the comments of the above code, we can see that there is a strong coupling between movielister and colondelimitedmoviefinder (which can enable any type that implements the moviefinder Interface), as shown in:
Figure 1
This makes it difficult for movielist to be released as a mature component, because in different application environments (including when the same software system is used by different users ), the movie finder that it depends on may vary widely. Therefore, in order to achieve true component-based development, there must be a mechanism that can meet the following two requirements at the same time:
(1) Remove the strong dependency of movielist on the specific movefinder type (dependency during compilation ).
(2) provide a correct moviefinder instance for movielist during running.
In other words, the dependency between movielist and moviefinder is generated at runtime (this dependency is "injected" at runtime at a suitable time ), this is probably the term dependency injection. In other words, we mentioned removing strong dependencies. This does not mean that the dependency between movielist and moviefinder does not exist. In fact, movielist also needs services provided by a certain type of moviefinder in any case, we just pushed back the creation time of this dependency, from the compiler to the runtime.
Dependency is widely used in OO programs. As long as type A uses type B instances, type A depends on type B. The content I mentioned above is to abstract the concept to the perspective of service users and service providers, which is also in line with the current SOA design philosophy. In another abstract way, we can regard movielist as the main system we want to build. moviefinder is the Plugin in the system, and the main system does not depend on any plug-in, however, once the plug-in is loaded, the main system should be able to accurately call the functions of the appropriate plug-in.
In fact, whether it is service-oriented programming mode or plug-in-based framework programming, in order to achieve loose coupling (between service callers and providers or between frameworks and plug-ins ), interface-oriented programming must be implemented in the necessary locations. On this basis, there should also be a convenient mechanism to bind runtime between specific types, which is the problem that Di should solve.

2. Implementation of di
As shown in figure 1 above, if our system implements dependency injection, the dependency between components is changed to Figure 2:
Figure 2
To put it bluntly, a container is provided to complete the creation of a specific serviceprovider (2) bind serviceuser to the runtime of serviceprovider. Next we will look at the implementation of three typical dependency injection methods in sequence. In particular, to understand the dependency injection mechanism, the key is to understand the container implementation method. The container reference implementation provided later in this article is the code of Mr. Huang zhongcheng. I only added some key notes here.

2.1 constructor injection (constructor injection)
We can see that in the entire dependency injection data structure, the important types involved are serviceuser, serviceprovider, and aggreger. The constructor mentioned here, it refers to the serviceuser constructor. That is to say, when constructing a serviceuser instance, the real serviceprovider is passed to him:

1 class movielister
2 {
3 // other content, omitted
4
5 Public movielister (moviefinder finder)
6 {
7 This. Finder = finder;
8}
9}

Next, let's take a look at how the aggreger should be built:

1 private mutablepicocontainer configurecontainer (){
2 mutablepicocontainer Pico = new defaultpicocontainer ();
3
4 // The following is the process of putting both serviceprovider and serviceuser into the container. In the future, the container will provide the completed dependency injection instance of serviceuser,
5 // The instance parameters and type parameters used are generally read from the configuration file. Here is a simple method.
6 // all the dependency injection methods will have a similar container initialization process. This code will not be repeated in the following sections.
7 parameter [] finderparams = {New constantparameter ("movies1.txt ")};
8 Pico. registercomponentimplementation (moviefinder. Class, colonmoviefinder. Class, finderparams );
9 Pico. registercomponentimplementation (movielister. Class );
10 // at this point, the container is loaded with two types. The movielister that does not provide the constructor will depend on the input parameter type defined in the constructor.
11 // search for a type match to initialize the structure.
12 Return Pico;
13}

It should be emphasized that the dependency does not disappear, but is delayed until the container is built. As you can see in section 2, the container itself (more accurately, it is the building process of a container running instance) is dependent on both serviceuser and serviceprovoder. Therefore, in such an architecture, serviceuser, serviceprovider, and container are stable and there is no dependency between them; all dependencies and changes are encapsulated in the creation process of container instances, which conforms to our understanding of service applications. In addition, in actual development, we usually use configuration files to assist in the creation of container instances, so that this variability is excluded from the compilation period.
Even if no code is provided, you can guess that the container class must have a method such as getinstance (type T, this method returns a movielister that has been injected. A simple application is as follows:

1 Public void testwithpico ()
2 {
3 mutablepicocontainer Pico = configurecontainer ();
4 movielister Lister = (movielister) Pico. getcomponentinstance (movielister. Class );
5 movie [] movies = Lister. moviesdirectedby ("Sergio Leone ");
6 assertequals ("once upon a time in the West", movies [0]. gettitle ());
7}

The most important thing above is the call to Pico. getcomponentinstance. The aggreger will call the movielister constructor at this time. The constructor parameter is then passed through Pico. registercomponentimplementation (moviefinder. class, colonmoviefinder. class, finderparams) specifies the actual serviceprovider -- colonmoviefinder. See the container reference code below:

Construct the pseudo code of the container required for Injection

 

2.2 setter injection (Set Value injection)
This injection method is similar to constructor injection. The only difference is that the constructor injects the constructor during calling, and it injects the constructor by assigning values to the attributes. No wonder picocontainer and spring both support these two injection methods at the same time. Spring provides better support for configuration through XML, and also makes it more common to use the set-value injection method in Spring:

1 <beans>
2 <bean id = "movielister" class = "Spring. movielister">
3 <property name = "Finder">
4 <ref local = "moviefinder"/>
5 </property>
6 </bean>
7 <bean id = "moviefinder" class = "Spring. colonmoviefinder">
8 <property name = "FILENAME">
9 <value> movies1.txt </value>
10 </property>
11 </bean>
12 </beans>

The following provides a reference implementation for containers that support Value-Setting Injection. You can compare it with the constructor injection container. The difference is very small. The main difference is that, when obtaining the object instance (getinstance), the former obtains the constructor information of the type to be created through reflection, and then searches for the information in the container based on the type of parameters input by the constructor, and construct an appropriate instance. The latter obtains all attributes of the type to be created through reflection, and then searches for instances of the corresponding type in the container based on the type of the attribute.

Implement pseudo code for containers with set-value Injection

2.3 interface injection (interface injection)
This is the most elegant method of dependency injection. To implement interface injection, serviceprovider must first provide an interface definition:

1 public interface injectfinder {
2 void injectfinder (moviefinder finder );
3}

Next, serviceuser must implement this interface:

1 class movielister: injectfinder
2 {
3 Public void injectfinder (moviefinder finder ){
4 This. Finder = finder;
5}
6}

The container needs to call the inject method to complete the injection process according to the interface definition. Here we will not go into details. The general principle is no different from the preceding two dependency injection modes.

2.4 besides Di, there are also service locator
The dependency injection mentioned above is only one way to eliminate the dependency between serviceuser and serviceprovider, and another method: service locator ). That is to say, servicelocator is responsible for providing specific serviceprovider. In this case, serviceuser not only depends on the service interface, but also servicecontract. It is still the first example of the movie enumerator. If you use service locator to remove dependencies, the entire dependency should be shown in:
Figure 3
It is also easy to use. initialize servicelocator at an appropriate location (for example, before a group of related services are called) and use servicelocator to return the serviceprovider instance:

1 // service locator Initialization
2 servicelocator locator = new servicelocator ();
3locator. loadservice ("moviefinder", new colonmoviefinder ("movies1.txt "));
4servicelocator. Load (Locator );

5 // use the service definer
6 // In fact, this usage method reflects the biggest difference between the service positioner and the dependency injection mode: serviceuser needs to display the call servicelocator to obtain the desired service object;
7 // dependency injection is implicitly completed by the container.
8 moviefinder finder = (moviefinder) servicelocator. getservice ("moviefinder ");
9

Because of the dependence of serviceuser on servicelocator mentioned above, from improving the independence of the module (for example, you may give the serviceuser or serviceprovider you constructed to a third party, dependency injection may be better, which is probably why most IOC frameworks use di. The biggest advantage of servicelocator is that it is very simple to implement. If your application is not complex enough to use an IOC framework, you may try to use it.

3. Services in a broad sense
The concepts of serviceuser and serviceprovider are mentioned in many parts of this article. Here, "service" is a very broad concept, at the syntax level, it refers to the most common dependency. (If type A has a variable of type B, type A depends on Type B ). If you understand a service as a service concept in WCF or web service, you will find that all the technical means mentioned above are meaningless. In terms of WCF, the client and server are loosely coupled with contract. In fact, this also illustrates the advantages of SOA applications from another perspective.

References:
Object Builder application block

Dependence Injection)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.