Next, we need to explain how to use di to make the delivery routines have the ability to switch between instances in real time.
Import the sequence example-di version
So that the authenticationservice type does not have to use the emailservice or initiate messageservice to send the sender certificate during the validity period, we must take a look at some of these other actions to open their original password-coupled relationship-or say "decoupling 」. There is a very effective tool for decoupling: interface ).
To be more clear, the original authenticationservice depends on the specific implementation type to send the certificate (such as emailservice). Now we want it to depend on a certain interface, this interface defines the actions that must be included in the work of sending the certificate. Since the interface is only a specification and does not contain any actual work, any kind of interface can be connected to authenticationservice as long as this specification is implemented, complete the sending of the certificate. With the middleware interface, developers can click 「Compile programming on interfaces rather than just practices.(Program to an interface, not an implementation) 3. Keep the components in each part of the application program "a little sticky, not too sticky, it is regarded as the goal of coupling between two nodes.
Extract Interface)
Start manual modification! First, we need to abstract emailservice and receivmessageservice (Abstract action), that is, to extract their common features and put them in an interface, make these common features into a rule, and then implement the rule separately from the concrete type. The following program example is the result after reconstruction, which contains an interface and two practices are similar. I use console in a different send method. the writeline method generates different response strings to facilitate the actual response (this example is a console-type application program example ).
Interface imessageservice
{
Void send (User user, string MSG );
}
Class emailservice: imessageservice
{
Public void send (User user, string MSG)
{
// Send a email to the specified user (omitted)
Console. writeline ("send email to the user, transmission content:" + MSG );
}
}
Class implements messageservice: imessageservice
{
Public void send (User user, string MSG)
{
// Send a response to the specified user (omitted)
Console. writeline ("sending response to the user, sending content:" + MSG );
}
}
It may be clearer to look at the case types:
|
Category 1-2: extract the class behind the common interface |
After the interface is extracted, if the premise is met, the authenticationservice can depend on this interface, without the need to perform a different operation based on specific conditions. To facilitate the comparison, I will list all the programs before and after the modification:
Class authenticationservice
{
// The original format is as follows:
Private parameter messageservice msgservice;
Public authenticationservice ()
{
This. msgsevice = new inclumessageservice ();
}
// Change it to the following:
Private imessageservice msgservice;
Public authenticationservice (imessageservice Service)
{
This. msgservice = service;
}
}
The difference after modification is as follows:
- Private component msgservice type: before modification, it is a specific type (emailservice or receivmessageservice), and after modification, it is the imessageservice interface.
- Constructor: before modification, you can directly create a response line for a specific type of object and assign the object token to the private employee msgservice; after the modification, an imessageservice interface should be added to the test and the test should be assigned to the private employee msgservice.
Control Anti-reverse (IOC)
Now, authenticationservice does not rely on specific practices, but only the imessageservice interface. However, the interface is just a standard, and there is no actual work, that is, we cannot perform this operation (we can't pass the manual operation ):
Imessageservice msgservice = new imessageservice ();
So where does an object come from? The answer is that the authentication mechanism of authenticationservice is passed in. Note that there is an important implication here: for non-di versions of authenticationservice, do not use the new operator to create an object for a specific interest service and control the lifecycle of the object; in the authenticationservice of di version, this control right is handed over to the external caller (main program) for further explanation. The dependency is removed, and "the anti-dependency is controlled 」.
The last thing to modify is the main program (mainapp ):
Class mainapp
{
Public void login (string userid, string PWD, string messageservicetype)
{
Imessageservice msgservice = NULL;
// Determine the information service object to be created by comparing strings.
Switch (messageservicetype)
{
Case "emailservice ":
Msgservice = new emailservice ();
Break;
Case "When messageservice ":
Msgservice = new inclumessageservice ();
Break;
Default:
Throw new argumentexception ("Invalid Response Message Service type! ");
}
VaR authservice = new authenticationservice (msgservice); // inject dependent objects
If (authservice. twofactorlogin (userid, PWD ))
{
// There is no change in this process, so it is omitted.
}
}
}
The last modification is the main program (mainapp): Class mainapp {public void login (string userid, string PWD, string messageservicetype) {imessageservice msgservice = NULL; // determine the information service object to be created by comparing strings. Switch (messageservicetype) {Case "emailservice": msgservice = new emailservice (); break; Case "inclumessageservice": msgservice = new inclumessageservice (); break; default: throw new argumentexception ("Invalid Response Message Service type! ");} Var authservice = new authenticationservice (msgservice); // inject dependent object if (authservice. twofactorlogin (userid, PWD) {// there is no change in this place, so it is omitted .}}} now, the main program will attempt to create an authentication service object. Then, when the authenticationservice object is created, the authentication service object will be copied to its creation mechanism. This method of injecting dependent objects into another object through the construction mechanism is a common method of di, this method also has a name called constructor injection ). "Structured injection" is a method for implementing Di. Chapter 1 will introduce it further. The dependencies between different types are as follows. Please compare it with the first round of the previous set to see the difference between the two (in order to avoid turning the Middle arrow over the previous round, I have removed the irrelevant Role user class ).
|
Sequence 1-3: Type Dependent Sequence after di version |
You will find that the dependency in the previous set depends on the upper and lower layers. Alternatively, the High-Level mode is based on the lower-level mode. This only meets a small part of the dependency inversion principle; dip in the prerequisite s.o.l. I. d. Five principles. Dip refers:
- The high-concurrency mode should not be based on the low-concurrency mode; they should all be based on the asynchronous actions ).
- Abstract operators should not be implemented based on actual conditions. Actual operators should be abstracted based on actual conditions.
From generation 1 to 3, it can be found that the di version's routine format already complies with the "dependency anti-dependence principle 」. The imessageservice interface refers to the abstract structure, while the High-Level module authenticationservice and the low-level module depend on the abstract structure. This di version has a good practice, that is, users will come up with new requirements later, in addition to email and notification, you also need to add the dynamic notification service for the platform ), this allows you to push your ICP filing information to the mobile app. At this time, you only need to add a new class (which may be named pushmessageservice) and make this class imessageservice, and then slightly change the mainapp to complete the process, authenticationservice does not need to be modified at all. Simply put, applications are more likely to crash.
Of course, the programming method of this example still lacks the following: it compares strings to determine which information server to establish. Imagine if there are 10 other types of information services to be supported, isn't the switch... In the case area too long? It would be even better if there is a complex object that can help us develop such special applications and create objects. This part will be explained in Chapter 1 "Di container.
When should di be used?
Once you start to feel the advantages of dynamic coupling, when designing applications, it is possible that coupling between all types of systems will remain unchanged. In other words, you want to define the interface first when you encounter any requirement, and then use the dependency injection method (for example, the "constructor injection" used in the previous example 」) to establish dependency between objects. However, there is no white lunch in the world, and zookeeper coupling is no exception. Each time you extract dependencies between different types, place them in another abstract, and inject dependent objects at specific times, in fact, this kind of operation will produce some external costs. It is not a good idea to design all objects to be inserted and inserted at any time, regardless of the time.
To. net-based class library; hierarchical class BCL) as an example. This class contains many components, various components include many existing categories for our direct access. Every time you use the class differences of Bcl in a program, such as string, datetime, hashtable, etc., it is equivalent to adding some sort of dependency to the program. At this time, will one day you worry that your program may need to replace these Bcl classes with other classes? What if I found the original homepage on the website? The answer usually depends on your confidence in the constant changes of specific types/components, and the so-called "regular changes 」, it will also vary according to the type and size of the application program.
Similar to other third-party components found or hidden on the internet, I think many people will think. the class in the net Bcl should be much more specific, that is, it cannot be changed without modification, because the existing program cannot be connected or the program can be connected normally. Some of us may have a certain degree of confidence in other providers, the other part is the experience from the past. No matter how, when adding third-party components to applications, it is best to make a decision after careful estimation.
The following is a scenario where you may need to use or understand di technologies:
- If you are designing a framework or a type of others that can be reused, Di will be very useful.
- If you are developing an application, you need to manually import and switch some components during the operation, and DI can also be used on site.
- We hope that our program will be able to easily remove some of the undefined components (such as third-party components, at this time, it may be used in adapter mode ).
- You are taking over an application program and want to reduce the dependency on some components during refactor programming, this makes it easy for programmers to renew their programs.
The following are some scenarios that may not contract or should be more cautious with DI:
- DI can be used in small applications with very simple requirements, so it may be suspected that the scalpers use the scalpers.
- In large and zookeeper applications, if the interfaces are both zookeeper-coupled interfaces and di-injected dependent objects, it may be a little hard work for new members who later take over zookeeper. During the programming process, he may feel frustrated because he cannot determine which object is used in a certain place. For example, if you see the send method of calling the imessageservice interface in the program, you cannot trace the actual operation of the method to understand what will happen next, because the interface does not provide any practical work. If no one points or files are available, you can only get to know the dynamic route of the program in a single step when you encounter any questions, this really requires some patience.
- When refactoring is performed on the old program, it may be difficult to introduce di because the existing design has not been tested completely.
In summary, it is always difficult to use any technical skills without thinking about it.
(Waiting for Renewal)
From: https://leanpub.com/dinet