Dependency Injection control inversion Service locator mode Dependency injection inversion of controls service Locator Patterns | Super Classic

Source: Internet
Author: User

Dependency Injection control inversion Service locator mode Dependency injection inversion of the control IoC service Locator Patterns

Author/martin Fowler compilation/Transparency

The Java community has recently unleashed a flurry of lightweight containers that can help developers assemble components from different projects into an cohesive application. There is a pattern behind them that determines how the containers are assembled. People use a generalizing name to call this pattern: "Inversion of Control" (inversion of CONTROL,IOC). In this article, I'll delve deeper into how this pattern works, giving it a more descriptive name-"Dependency Injection" (Dependency injection) and comparing it to the service Locator mode. However, the difference between the two is not very important, and more importantly: the configuration and use of components should be separated from the-two mode is the goal of this.

There is an interesting phenomenon in the Enterprise Java World: There are a lot of people who devote a lot of effort to studying the alternatives to mainstream EE technology-nature, which mostly happens in the open source community. To a large extent, this can be seen as a developer's response to the cumbersome and complex approach of mainstream Java EE technology, but there are a lot of creative ideas that really offer some options. One of the problems that Java EE developers often encounter is how to assemble different program elements: if the Web controller architecture and the database interface are developed by different teams and have little knowledge of each other, how should you make them work together. Many frameworks have tried to solve this problem, and several frameworks have been developed in this direction, providing a more general "assemble each layer" of the program. Such frameworks are often referred to as "lightweight containers", Picocontainer and spring are in this column.

Behind these containers are some interesting design principles that play a role. These principles have gone beyond the scope of a particular container, even beyond the scope of the Java platform. In this article, I will initially reveal these principles. The paradigm I use is Java code, but like most of my articles, these principles apply to other OO environments, especially. NET.

Components and Services

"Assembler element," The topic immediately dragged me into a tricky terminology question: how to differentiate between "service" and "component" (component). You can effortlessly find out the long-winded definitions of these two words, and the conflicting definitions will make you feel my dilemma. In view of this, for these two severely abused vocabulary, I will be the first

First describe their usage in this article.

A "component" is a unit of software that is used by other applications beyond the control of the author, but which cannot modify the component. In other words, an application that uses a component cannot modify the source code of a component, but can extend it through some way reserved by the author to alter the behavior of the component.

There are some similarities between services and components: they will all be used by external applications. In my opinion, the biggest difference between the two is that the component is used locally (such as a jar file, assembly, DLL, or source code import), and the service is to be used remotely (such as a Web service, message system, RPC, or socket) through a synchronous or asynchronous-remote interface.

In this article, I will mainly use the word "service", but most of the logic in this paper also applies to local components. In fact, for easy access to remote services, you often need some kind of local component framework. However, "components or services" such a phrase is too troublesome, and the word "service" is now very popular, so this article will use "service" to refer to the two.

a simple example

To better illustrate the problem, I would like to introduce an example. Like all the examples I used before, this is a super-simple example: it's very small, too small to be true, but it's enough to help you see the truth and not get stuck in the mire of real examples.

In this example, I've written a component that provides a list of movies that are directed by a particular director. There is only one way to achieve this great feature:

Class Movielister ...

Public movie[] Moviesdirectedby (String Arg) {

List allmovies = Finder.findall ();

for (Iterator it = Allmovies.iterator (); It.hasnext ();) {

Movie movie = (movie) It.next ();

if (!movie.getdirector (). Equals (ARG)) It.remove ();

}

Return (movie[]) Allmovies.toarray (new Movie[allmovies.size ()));

}

As you can see, the implementation of this feature is extremely straightforward: the Moviesdirectedby method first asks the Finder (the movie Searcher) object (which we'll talk about later) to return all the movies that the latter knows, and then iterates through the list returned by the Finder object. and return a movie that is directed by a particular director. Very simple, but don't worry, this is just the whole example.

Scaffolding.

What we really want to look at is a Finder object, or how to connect a Movielister object to a particular Finder object. Why we are particularly interested in this issue. Because I want this beautiful Moviesdirectedby method to be completely independent of how the movie is actually stored. Therefore, this method can only refer to a Finder object, and the Finder object must know how to respond to the FindAll method. To help the reader understand more clearly, I have defined an interface for the Finder:

Public interface Moviefinder {

List FindAll ();

}

There is now no coupling between the two objects. However, when I'm actually looking for a movie, I have to involve a specific subclass of Moviefinder. Here, I put the code "involving specific subclasses" in the constructor of the Movielister class.

Class Movielister ...

Private Moviefinder finder;

Public Movielister () {

Finder = new Colondelimitedmoviefinder ("Movies1.txt");

}

The name of the implementation class shows that I'm going to get a list of movies from a comma-delimited text file. You don't have to worry about specific implementation details, just imagine such an implementation class.

If this class is only used by me, everything is fine. But what if my friends are impressed with this wonderful feature and want to use my program? If they also keep the movie list in a comma-delimited text file, and also name the file "Movie1.txt", then everything is fine. If they just renamed the file, I can also get the file name from a profile, which is also easy. However, if they store the movie list in a completely different way-such as a SQL database, an XML file, a Web service, or a text file in another format. In this case, we need to use another class to get the data. Since the Moviefinder interface has been defined, I can not modify the Moviesdirectedby method. However, I still need some way to get an instance of the appropriate Moviefinder implementation class.


Figure 1: Dependency When creating a Moviefinder instance directly in the Movielister class

Figure 1 shows the dependencies in this case: the Movielister class depends on both the Moviefinder interface and the specific implementation class. We certainly want the Movielister class to depend only on the interface, but how do we get an instance of the Moviefinder subclass?

In patterns of Enterprise application Architecture, we refer to this as a "plug-in" (plugin): The Moviefinder implementation class is not included in the program at compile time, Because I don't know which implementation class my friend will use. We want the Movielister class to work with any of the implementation classes of Moviefinder, and to allow insertion of specific implementation classes at runtime, where the insert action is completely out of my control (the original author). The question here is: How to design this connection process so that the Movielister class works with its instances without knowing the implementation class details.

To generalize this example, in a real system, we might have dozens of services and components. At any time, we can always abstract the situation of using components, communicating with specific components through interfaces (if the component does not design an interface, it can also be communicated through the adapter). However, if we want to deploy this system in different ways, we need to use a plug-in mechanism to handle the interaction between services so that we can use different implementations in different deployment scenarios.

So the core question now is how to combine these plugins into one application. This is the main problem faced by newborn lightweight containers, and their means of solving this problem are, without exception, the control reversal (inversion of controls) mode.

Control Reversal

The authors of several lightweight containers proudly said to me: These containers are very useful because they achieve "inversion of control". I am puzzled by this: control reversal is a feature common to the framework, and if you think that these lightweight containers are different just because you use control inversion, it's like saying "My car is different because it has four wheels".

The key to the problem is that they reverse the control. My first exposure to the control reversal is for the user interface of the master control. The early user interface is completely controlled by the application, and you pre-design a series of commands, such as "Enter Name",

"Input address" and so on, the application output prompt information, and retrieve the user's response. In a graphical user interface environment, the UI framework is responsible for executing a main loop, and your application simply provides event handlers for each area of the screen. Here, the main control of the program has been reversed: from the application to the framework.

For these new containers, they reverse the "how to locate the specific implementation of a plug-in." In the previous simple example, the Movielister class is responsible for locating the specific implementation of the Moviefinder-it instantiates a subclass of the latter directly. In this way, Moviefinder is not a plug-in, because it is not inserted in the application during the run time. These lightweight containers use a more flexible approach, as long as the plug-in follows certain rules, a standalone assembly module can "inject" the specific implementation of the plug-in into the application.

So, I think we need to give this pattern a more descriptive name-the name "inversion of control" is too broad and often confusing. After discussing with a number of IOC enthusiasts, we decided to call this model "dependency injection" (Dependency injection).

Below, I'll start by introducing several different forms of the dependency injection pattern. But before I do, I would like to point out that, to eliminate the application's dependency on plug-in implementations, dependency injection is not the only option, and you can get the same effect with service Locator mode. After introducing dependency injection mode, I will also talk about service Locator mode.

several forms of dependency injection

The basic idea of Dependency injection mode is to use a separate object (assembler) to obtain an appropriate implementation of Moviefinder and assign its instance to a field of the Movielister class. In this way, we get the dependency graph shown in Figure 2:


Figure 2: Dependency after a dependency injector is introduced

There are three main forms of dependency injection, and I will call them construction sub-injection (Constructor injection), SetPoint injection (Setter injection), and interface injection (Interface injection). If you have read some recent discussions about the IOC, it is easy to see that the three injection forms are type 1 IOC (interface injection), type 2 IOC (value method injection), and Type 3 IOC (construct sub-injection). I found that the numbers were often difficult to remember, so I used

Here's how to name it.

using Picocontainer to construct sub-injection

First, I want to show the reader how to complete the dependency injection with a lightweight container called Picocontainer . It started here mainly because I was very active in a few colleagues at the ThoughtWorks company in the Picocontainer development community--yes, it could be some kind of partiality.

Picocontainer the "How to inject the Moviefinder instance into the Movielister class" by constructing the child. Therefore, the Movielister class must declare a constructor that contains all the elements that need to be injected:

Class Movielister ...

Public movielister (Moviefinder Finder) {

This.finder = Finder;

}

The Moviefinder instance itself will also be managed by Picocontainer, so the name of the text file can also be injected by the container:

Class Colonmoviefinder ...

Public Colonmoviefinder (String filename) {

This.filename = filename;

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.