Spring and Other lightweight containers can help developers assemble components from different projects into a cohesive application .. There is the same mode behind them. This mode determines how these containers assemble components. People refer to this pattern with a big name: "inversion ofcontrol, IOC ). give it a more descriptive name-dependency injection, and compare it with the service locator pattern. However, the difference between the two is not very important. More importantly, the component configuration and usage should be separated. This is the goal of both modes. (Applications are assembled by components, but independent of the component implementation class, which decouples the implementation of applications and components)
A common problem for J2EE developers is how to assemble different program elements: if the web Controller Architecture and database interfaces are developed by different teams, they have almost no knowledge of each other, how should you make them work together? Many frameworks have tried to solve this problem. There are several frameworks that develop in this direction and provide a more general solution for "assembling components at different layers. Such a framework is usually called a "lightweight container". Both picocontainer and spring are in this column.
The so-called "component" refers to a software unit that will be used by other applications beyond the control of the author, but the latter cannot
Component. That is to say, an application that uses a component cannot modify the source code of the component, but it can
To change the behavior of components.
Services and components have some similarities: they will be used by external applications. In my opinion, the biggest difference between the two
Is: components are used locally (such as jar files, assembly, DLL, or source code import), and services are
-- Synchronous or asynchronous -- remote interface for remote use (such as Web Service, message system, RPC, or
Socket ).
In a real system, we may have dozens of services and components. At any time, we
You can always Abstract The use of components and communicate with specific components through interfaces (if the component does not have an interface designed,
You can also use the adapter to communicate with it ). However, if we want to deploy the system in different ways, we need to use plug-ins.
Mechanism to process the interaction process between services, so that we can use different implementations in different deployment solutions.
Therefore, the core issue is: how to combine these plug-ins into an application? This is the new lightweight container.
The main problems they face, and the means they solve this problem are, without exception, the inversion of control)
Mode.
The authors of several lightweight containers proudly told me that these containers are very useful because they implement "control inversion ". This
I am deeply confused by the words of this example: Control reversal is a common feature of the Framework. If we use control reversal, we think that this
Some lightweight containers are just like saying, "My car is different because it has four wheels ".
I think we need to give this model a name that can better describe its characteristics-the name "control reversal" is too generic,
It is often confusing. After discussing with many IOC enthusiasts, we decided to call this mode "dependency injection"
(Dependency injection ).
There are three main forms of dependency injection: constructor injection and value setting.
Setter injection and interface Injection)
The biggest benefit of dependency injection is that it eliminates the dependency of application classes on specific component implementation classes. This way
First, I can give the application class to my friends and let them insert a suitable
Component implementation. However, the dependency injection mode is not the only one that breaks this dependency layer.
Another method is to use the service locator mode.
The basic idea behind the service locator mode is: an object (that is, a service locator) knows how to obtain an application.
All services required by the program. That is to say, in our example, the service locator should have a method to obtain
Moviefinder instance. Here, I implement the servicelocator class as a singleton registry, so the application can
Obtain an instance of a component through servicelocator during instantiation.
Dependency injection and service locator are not mutually exclusive. You can use them at the same time,
This is an example of the aveon framework. Avron uses a service locator, but "How To Get The locator" Information
The component is notified through injection.
Service locator vs. Dependency Injection
First, we are faced with the choice between service locator and dependency injection. Note that even though I
The previous simple example is not adequate. In fact, both models provide basic decoupling capabilities-regardless
In which mode, the application code does not depend on the specific implementation of the service interface. The most important difference between the two is:
Specific implementation methods provided to the application code. When the service locator mode is used, the application
The Code directly sends a message to the service locator, which explicitly requires the implementation of the service. The dependency injection mode is used.
When the application code does not send explicit requests, the service implementation will naturally appear in the application code.
"Control reversal
Control inversion is a common feature of the framework, but it also requires you to pay a certain price: it increases the difficulty of understanding and provides debugging
It brings some difficulties. Therefore, I will try to avoid using it unless necessary. This does not mean that anti-control
However, I think a more intuitive solution (such as the service locator Mode)
Suitable.
The dependency injection mode helps you see dependencies between components: you only need to observe the dependency injection machine.
(For example, constructing a child), you can master the entire dependency. When using the service locator mode, you must
Search for service locator calls everywhere in the code.
A key difference is that when the service locator mode is used, the service user must rely on the service locator.
The positioner can hide users' dependencies on specific service implementations, but you must first see the locator itself. Therefore, answer the question
The case is clear: whether to choose service locator or dependency injection depends on
Depends on whether it will cause you trouble.
One common reason people tend to use the dependency injection model is that it simplifies testing. Here
The key is: Out of test needs, you must be able to easily implement "real service implementation" and "pseudo" components for testing"
Switch. However, from this perspective, the dependency injection mode and service locator
There is no big difference in the mode: both of them can support the insertion of "pseudo" components. Many people have
"Dependency injection mode is more conducive to testing", I guess it is because they have not worked hard to guarantee the service
The value of the biter. This is where continuous testing works: if you cannot easily use some "pseudo" components
This means that your design has encountered serious problems.
Of course, if the component environment is very aggressive (just like the EJB framework), the testing problems will become more serious. My
The point is: try to minimize the impact of such frameworks on application code, especially not to make "edit-Execute"
. Replacing heavyweight components with the plug-in mechanism will be of great help to the testing process.
Key to practices such as test driven development and TDD
Construct sub-injection vs. Set Value Method Injection
The choice between value function injection and constructor injection is quite interesting because it reflects some of the more common questions about object programming.
Question: Where should I fill in the object field to construct a sub-or set a value method?
For a long time, my preferred practice is to create a complete and valid object in the constructor stage -- that is, in the constructor
Fill in the object field. The benefits of doing so can be traced back to Kent Beck in Smalltalk best practice patterns
Two modes described in this book: constructor method and constructor parameter method. With Parameters
The number constructor can clearly tell you how to create a legal object. If you create more than one valid object
You can also provide multiple constructor to illustrate different combinations.
Another advantage of constructing sub-Initialization is that you can hide any unchangeable field-as long as you do not provide a value setting method for it.
. I think this is very important: If a field should not be changed, "No value setting method for this field" will be clear.
This point is clearly illustrated. If you use the value setting method to complete initialization, the exposed value setting method may become permanent in your mind.
Far from pain
However, there are always exceptions in the world. If there are too many parameters, the constructor will appear messy, especially for those that do not support keyword Parameters
This is especially true for languages. Indeed, if the list of constructed sub-parameters is too long, it usually indicates that the object is too busy and should be split into several
Objects, but sometimes so many parameters are required.
If there are more than one way to construct a valid object, it is difficult to construct the sub-description information because
Only the number and type of parameters can be used to distinguish between them. This is the application of the factory method mode.
You can use a combination of multiple private constructor and value setting methods to complete your tasks. Classic factory method mode
They often appear in the form of static methods, and you cannot declare them in the interface. You can create a job
Factory, but it becomes another service entity. "Factory service" is a good technique, but you still need
The problem still persists when the factory object is instantiated.
If the input parameter is of a simple type like a string, it may cause some trouble to construct the sub-injection. Note using the set value method
You can describe the purpose of the parameter in the name of each set-value method. When you construct a sub-injection, you can only rely on
Location determines the role of each parameter, and it is much more difficult to remember the correct location of the parameter.
If an object has multiple constructor and there is an inheritance relationship between the objects, it will become particularly annoying. To make everything
To initialize the sub-class correctly, you must forward the sub-class constructor calls to the sub-class constructor and process your own parameters. This can be
This can cause further expansion of the constructor scale.
Despite these defects, we recommend that you first consider constructing subinjection. However, once the questions mentioned above are actually
You should be prepared to use the set value method for injection.
Code configuration vs. Configuration File
Another issue is relatively independent, but often associated with other issues: how to configure service assembly, through the configuration file
Is code Assembly directly? For most applications that need to be deployed in multiple places, a separate configuration file is more suitable.
Configuration files are almost all XML files, and XML is indeed suitable for this purpose. However, sometimes it is directly in the program code
It is easier to implement Assembly. For example, a simple application does not have many changes in deployment. In this case, a few pieces of code are used for configuration.
It is much clearer than the XML file.
In the Java World, we hear discord from the configuration file-each component has its own configuration file, and
The formats are also different. If you want to use a dozen of such components, You have to maintain a dozen of configuration files.
Annoying.
Here, my advice is: always provide a standard configuration method that allows programmers to easily use the same Programming Interface
Complete the configuration. Other configuration files only use them as an optional feature. With this programming interface, open
Users can easily manage configuration files. If you have compiled a component, the component user can choose how to manage it.
Configuration information: Use your programming interface to directly operate the configuration file format, or define their own configuration file format, and
Combine it with your programming interface.