Thoughts on the dependency injection Ioc/di

Source: Internet
Author: User
Tags table definition in domain

In recent days, I've been looking at a book called Dependency Injection in. NET, which mainly speaks of what is dependency injection, the advantages of using dependency injection, and so on. The various frameworks and usages of dependency injection on the net platform. At the beginning of this book, one of the important concepts in software engineering is to focus on separation (separation of concern, SoC). Dependency injection is not an end, it is a series of tools and means, and the ultimate goal is to help us develop loosely coupled (loose coupled), maintainable, testable code and programs. The practice of this principle is well-known interface-oriented, or abstract-oriented programming.

On what is dependency injection, there is a problem on stack overflow, how to explain dependency injection to a 5-year-old child, one of the highest scoring answers is:

When you go and get things out of the the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something mommy or Daddy doesn ' t want your to have. You might even is looking for something we don ' t even have or which had expired.

What are doing are stating a need, "I need something to drink with lunch," and then we'll make sure should Mething when your sit down to eat. "

Mapping to object-oriented programming development is: High-level (5-year-olds) should rely on the underlying infrastructure (parents) to provide the necessary services.

Writing loosely coupled code is easy to say, but actually writing it is a tight coupling.

Using examples to illustrate what might be more concise, first look at what kind of code is tightly coupled.

1 Bad implementations

The first step in writing loosely coupled code may be familiar to everyone, that is, layering the system. For example, the following classic three-tier architecture.

It is two things to finish the layer and realize it, not to say that the layers can be loosely coupled after they are divided.

1.1 Tightly coupled code

There are many ways to design a flexible, maintainable complex application, but the N-tier architecture is a familiar approach, and the challenge is to implement the N-tier architecture correctly.

Suppose you want to implement a very simple e-commerce site, to list the commodity lists, as follows:

The following is a concrete example of how to write a tightly coupled code in one step at a pace.

1.1.1 Data Access Layer

To implement the function of commodity list, the first is to write the data access layer, need to design database and table, the database table product structure designed in SQL Server is as follows:

Once the table is well designed, you can start writing code. In Visual Studio, create a new project called Dataaccesslayer, and add an ADO Data Model, at which time the visual The Studio Wizard automatically helps us generate the product entity and the ObjectContext DB operation context. So our Data Access layer is ready to write.

1.1.2 Business Logic Layer

The presentation layer can actually access the data access layer directly and get the product list through ObjectContext. However, in most cases, we do not directly show the data in db, but we need to deal with the data, for example, for members, we need to discount the price of certain items. That way we need the business logic layer to deal with these things that are relevant to the specific business logic.

Create a new class library, name Domainlogic, and then add a class named Productservice:

public class Productservice {    private readonly commerceobjectcontext objectContext;    Public Productservice ()    {        this.objectcontext = new Commerceobjectcontext ();    }    Public ienumerable<product> getfeaturedproducts (        bool iscustomerpreferred)    {        var discount = iscustomerpreferred. 95m:1;        var products = (from P in This.objectcontext                            . Products                        where p.isfeatured                        select p). AsEnumerable ();        Return from P in products                select New Product                {                    ProductId = P.productid,                    Name = p.name,                    Description = p.description,                    isfeatured = p.isfeatured,                    unitprice = p.unitprice * Discount                };    }

Now our business logic layer has been implemented.

1.1.3 Presentation Layer

Now implement the presentation logic, using ASP. NET MVC, in the controller of the index page, get the list of items and return the data to view.

Public ViewResult Index () {    bool Ispreferredcustomer = this         . User.IsInRole ("Preferredcustomer");    var service = new Productservice ();    var products =         service. Getfeaturedproducts (Ispreferredcustomer);    This. viewdata["Products") = Products;    return this. View ();}

The data returned in the controller is then displayed in the view:

1.2 Analysis

Now, according to the three-tier "architecture" Our code is written, and also meet the requirements. The structure of the whole project is as follows:

This should be the so-called three-tier architecture that we often write about. In Visual Studio, dependencies between layer three can be represented by project references.

1.2.1 Dependency Graphs

Now let's analyze the dependencies between the three layers, obviously, the above implementation, domianlogic need to rely on sqldataaccess, because domainlogic in the use of product this entity, And this entity is defined in the DataAccess layer. WebUI this layer needs to rely on domainlogic, because Productservice is in this layer, but also relies on dataaccess, because the product entity is also used in the UI, and now the whole system depends on it:

analysis of coupling of 1.2.2

The main purpose of using the three-tier structure is to isolate the concerns and, of course, one reason is testability. We should separate the domain model from the data Access layer and the presentation layer so that the changes in these two layers will not pollute the domain model. In large systems, this is important in order to isolate the different parts of the system.

Now look at the previous implementation, there is no modularity, there is no module can be isolated out of it. Now add a few new case to see if the system responds to these requirements:

Add a new user interface

In addition to WebForm users, you may also need a WinForm interface, now can we reuse the domain layer and the data access layer? As you can see from the dependency graph, there is no one module that relies on the presentation layer, so this change is easy to achieve. We just need to create a WPF rich client to do that. Now the entire system depends on the diagram as follows:

Replace a new data source

It may take a while for the entire system to be deployed to the cloud, using other data storage technologies such as the Azure Table Storage Service. Now, the entire access to the data protocol has changed, the way to access the Azure Table Storage Service is the HTTP protocol, and most of the previous. NET access data is based on the way ADO. And the way the data source is saved has changed, before it is the relational database, now it becomes the Key-value type database.

As can be seen from the above dependency graph, all layers depend on the data access layer, and if you modify the data access layer, the domain logic layer, and the presentation layer, need to be modified accordingly.

1.2.3 Questions

There are other problems in the code in addition to the strong coupling between the layers above.

    • The domain model appears to be written in the data access layer. So the domain model seems to depend on the data access layer. A class named product is defined in the data access layer, and this class should belong to the domain model layer.
    • The representation layer is incorporated into the logic that determines whether a user is a member. This business logic should be handled in the business logic layer, so it should also be placed in the domain model layer
    • Productservice because it relies on the data access layer, it also relies on information such as the database connection string configured in Web. config. This makes it necessary for the entire business logic layer to rely on these configurations to function properly.
    • In view, there are too many functional functions. He performs operations such as forced type conversions, string formatting, and so on, which should be done in the model shown in the interface.

The above may be the implementation of most of our code writing, UI interface layer to rely on the data access layer, sometimes lazy to directly reference this layer, because the entity is defined inside. The business logic layer is also dependent on the data access layer, which directly uses the entities within the data access layer within the business logic. This makes the entire system tightly coupled and can be tested poorly. Now let's see how we can modify such a system to make it loosely coupled to improve testability.

2 A better implementation

Dependency injection is a good solution to the problem above and can now be used to re-implement the previous system. The reason for this re-implementation is that the previous implementations did not seem to take the scalability and loose coupling into account at the outset, and it was difficult to achieve the desired effect by using refactoring methods. It may be possible for small systems, but it should be difficult for a large system.

In writing the code, to manage the dependencies, in front of the implementation of this, the code directly control the dependency: When Productservice needs a ObjectContext class, it seems that the new one directly, When HomeController needed a productservice, it was a straightforward new one, so it looked cool and convenient, which actually made the whole system very limited and became tightly coupled. The new operation actually introduces dependencies, and the idea of control inversion is to make our better management dependencies.

2.1 Loosely-coupled code2.1.1 Presentation Layer

First, from the presentation layer, the presentation layer is mainly used to show the data, and should not contain too much logic. In the view page of index, the code wants to be written like this

As you can see, it's a lot more neat than the previous performance layer code. It is clear that you do not need a type conversion, to achieve such a purpose, only need to let index.aspx this view inherit from System.web.mvc.viewpage<featuredproductsviewmodel>, When we create a view from a controller, we can make a selection and then generate it automatically. The entire information for the presentation is placed in the Summarytext field.

This introduces a view model (View-specific Models) that encapsulates the behavior of the views, which are simply pocos objects (Plain old CLR Objects). Featureproductsviewmodel contains a list of lists, each of which is a Productviewmodel class that defines some simple fields for data presentation.

Now in the controller, we just need to return the Featureproductsviewmodel object to the view. Like what:

Public ViewResult Index () {    var vm = new Featuredproductsviewmodel ();    return View (VM);

Now we're going to return to the empty list, and fill it out in the domain model, we'll look at the domain model layer.

2.1.2 Domain Logic Layer

Create a new class library, where bread contains pocos and some abstract types. Pocos is used to model domains, and abstract types provide abstractions as portals to the domain model of arrival. The principle of dependency injection is interface-oriented rather than specific class programming, which allows us to replace concrete implementations.

Now we need to provide data for the presentation layer. Therefore, the user interface layer needs to reference the domain model layer. A simple abstraction of the data access layer can take the repository pattern described in the patterns of Enterprise application architecture book. So define a Productrepository abstract class, and note that it is an abstract class in the Domain model library. It defines an abstract method for getting all specials:

Public abstract class productrepository{public    abstract ienumerable<product> getfeaturedproducts ();}

The product class of this method only defines the basic information such as the name and unit price of the product. The entire diagram is as follows:

Now for the presentation layer, the index method in HomeController should use the Productservice instance class to get a list of items, perform a price discount, and convert the product to a Productviewmodel instance, and add the instance to Featuresproductsviewmodel. Because Productservice has a constructor with a type of productreposity abstract class, an instance of the Productreposity abstract class can be implemented here through constructor injection. The biggest difference here and before is that we don't use the new keyword to immediately new an object, but instead pass the constructor to the concrete implementation.

Now look at the presentation layer code:

public partial class homecontroller:controller{    private readonly productrepository repository;    Public HomeController (productrepository repository)    {        if (repository = = null)        {            throw new ArgumentNullException ("repository");        }        This.repository = repository;    }    Public ViewResult Index ()    {        var productservice = new Productservice (this.repository);        var vm = new Featuredproductsviewmodel ();        var products = Productservice.getfeaturedproducts (this. User);        foreach (var product in products)        {            var productvm = new Productviewmodel (product);            Vm. Products.add (PRODUCTVM);        }        return View (VM);}    }

In the HomeController constructor, an instance of the Productrepository abstract class is passed in, and the instance is saved in the Repository object of the defined private, read-only productrepository type. This is typically injected through a constructor function. In the index method, the main function in the Productservice class of getting data is actually proxied through the incoming repository class.

The Productservice class is a purely domain object, implemented as follows:

public class productservice{    private readonly productrepository repository;    Public Productservice (productrepository repository)    {        if (repository = = null)        {            throw new ArgumentNullException ("repository");        }        This.repository = repository;    }    Public ienumerable<discountedproduct> getfeaturedproducts (IPrincipal user)    {        if (user = = null)        {            throw new ArgumentNullException ("user");        }        Return from P in                        this.repository.GetFeaturedProducts ()                select p.applydiscountfor (user);}    }

You can see that Productservice is also through the method of constructor injection, save the implementation of the Productreposity abstract class instance, and then use the Getfeatureproducts method in this instance to get the raw list data, and then to discount processing, And then realize their own getfeaturedproducts method. In the Getfeaturedproducts method, the difference is that the current parameter is IPrincipal, not the previous bool type, because judging the user's condition, this is a business logic that should not be dealt with in the presentation layer. IPrincipal is a type in the BCL, so there is no additional dependency. We should be based on interface programming IPrincipal is a standard way for application users.

Here, IPrincipal is passed as a parameter to a method, and then the implementation is invoked in a way that relies on the method injected in the injection. As with constructor injection, the internal implementation is also delegated to the incoming dependent object.

Now we have only two blocks left to deal with:

    • Without the implementation of Productrepository, this is easy to implement, and then put in the data access layer to deal with, we only need to create a concrete implementation of the Productrepository data access class.
    • By default, ASP. NET MVC wants the controller object to have its own default constructor, because we have added a new constructor in HomeController to inject the dependency, so the MVC framework does not know how to resolve the creation of the instance because it has dependencies. This problem can be solved by developing a Icontrollerfactory object that can create a specific productrepositry instance and then pass it on to HomeController here.

Now our domain logic layer has been written. At this level, we only manipulate the domain model objects, as well as the base objects in the. NET BCL. The model is represented by Pocos, named product. The domain model layer must be able to communicate with the outside world (database), so an abstract class (Repository) is required to complete this function and, if necessary, to replace the specific implementation.

2.1.3 Data Access Layer

Now we can implement specific data access layer logic using LINQ to entity. Because you want to implement the Productrepository abstract class of the domain model, you need to introduce the domain model layer. Note that this dependency becomes the domain model layer of the data access layer dependency. Contrary to the previous, the code is implemented as follows:

public class sqlproductrepository:domain.productrepository{    private readonly commerceobjectcontext context;    Public sqlproductrepository (String connstring)    {        this.context =            new Commerceobjectcontext (connstring);    } Public    override Ienumerable<domain.product> getfeaturedproducts ()    {        var products = (from P in this.context.Products                        where p.isfeatured                        select p). AsEnumerable ();        Return from P in the Products                select P.todomainproduct ();    }}

It is important to note that in the domain model layer, we define a domain model called product, and then the Entity Framework in the data access layer helps us to generate a data access layer entity called product. He corresponds to product table one by one in db. So when the method returns, we need to convert the type from product in db to the Pocos product object in the domain model.

Product in domain model is an object of type Pocos that contains only some of the basic fields needed in the domain model, and the Product object in DataAccess is an entity mapped to DB that contains all the fields of the Product table definition in the database , in the data representation layer we define a model for Productviewmodel data presentation.

The conversion between these two objects is simple:

public class product{Public    domain.product todomainproduct ()    {        domain.product p = new domain.product ();        P.name = this. Name;        P.unitprice = this. UnitPrice;        return p;    }}
2.2 Analysis2.2.1 Dependency Graphs

Now, the entire system's dependency graph is as follows:

Both the presentation layer and the data access layer rely on the domain model layer, so that in the previous case, if we add a new UI interface, and replace the storage and acquisition of a data source, only the corresponding layer of code can be modified, the domain model layer remains stable.

2.2.2 Timing Diagram

The timing diagram for the entire system is as follows:

When the system starts, A custom Controller factory class was created in Global.asax, the application stores it locally, and when the page request comes in, the program starts the Createcontroller method of the factory class and finds the database connection string in Web. config and passes it to the new Sqlproductrepository the instance, and then injects the sqlproductrepository instance into Homecontroll and returns.

The instance method called HomeController is then applied to create a new Productservice class and pass in the sqlproductrepository through the constructor. Productservice Getfeaturedproducts method Proxy to sqlproductrepository instance to implement.

Finally, the Viewresult object that fills the Featuredproductviewmodel is returned to the page, and MVC is displayed appropriately.

new structure of 2.2.3

In the implementation of 1.1, a three-tier architecture was adopted, and in the improved implementation, a presentation model layer was added to the UI layer and the domain model layer. Such as:

The controllers and ViewModel are moved from the presentation layer to the presentation model layer, leaving only the view (. aspx and. ascx files) and aggregate root objects (composition root) in the presentation layer. The reason for this is that it makes it possible for the presentation layer to be configurable while the rest of the part can be kept as it is.

3. Conclusion

Accidentally we wrote out the tightly coupled code, sometimes thought layered can solve the problem, but most of the time, there is no right to achieve layering. One reason why it is easy to write tightly coupled code is because the programming language or development environment allows us to instantiate one by using the New keyword as long as it is necessary to have a fresh instance object. If we need to add dependencies, Visual Studio can sometimes automatically help us add references. This makes it easy for us to make mistakes, to use the new keyword, which is likely to be introduced, and to add references that produce dependencies.

The best way to reduce the dependency and tight coupling introduced by new is to use a constructor injection that relies on this design pattern: that is, if we need a dependent instance, it is injected through the constructor. The implementation in the second section demonstrates how to target abstractions rather than specific programming.

Constructor injection is an example of inversion control because we reverse the control of dependencies. Instead of creating an instance with the New keyword, this behavior is delegated to a third-party implementation.

Hopefully this article will give you an idea of how to really implement a three-tier architecture, write loosely coupled, maintainable, testable code to provide some help.

Http://stackoverflow.com/questions/1362962/when-not-to-use-ioc-and-di

Reference http://www.cnblogs.com/yangecnu/p/Introduce-Dependency-Injection.html

Thoughts on the dependency injection Ioc/di

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.