Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of dependency inversion principle DIP in C #

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces the example analysis of relying on inversion principle DIP in C#, which is very detailed and has certain reference value. Friends who are interested must finish reading it!

I. Preface

Let's take a look at the traditional three-tier architecture, as shown in the following figure:

We can see from the above figure that in the traditional three-tier architecture, the layers are interdependent, the UI layer depends on the BLL layer, and the BLL layer depends on the DAL layer. The purpose of layering is to achieve "high cohesion and low coupling". The traditional three-tier architecture has only high cohesion and no low coupling, and the relationship between layers is a strong dependency, which is also a disadvantage of the traditional three-tier architecture. This top-down dependency will lead to cascade modification. If the lower level changes, all the upper layers may need to be modified, and this traditional three-tier architecture is also very difficult to achieve collaborative development of the team. Because the upper layer function depends on the implementation of the lower layer function, if the lower function is not developed, the upper layer function cannot be carried out.

The traditional three-tier architecture does not follow the principle of relying on inversion (DIP) to design, so there will be the above problems.

II. Dependency inversion

Dependency inversion (DIP): the abbreviation of Dependence Inversion Principle has two main meanings:

High-level modules should not rely on low-level modules, both should rely on their abstraction.

Abstraction should not depend on concrete, which should depend on abstraction.

Let's first explain the first sentence: high-level modules should not directly rely on the concrete implementation of low-level modules, but should rely on the abstraction of low-level modules, that is to say, dependencies between modules occur through abstraction. Direct dependencies should not occur between implementing classes, and their dependencies should be generated through interfaces or abstract classes.

To explain the second sentence: interfaces or abstract classes should not depend on the implementation class. For example, if we want to write BLL layer code, directly to achieve the function, wait until the completion of the development found that there is no use of dependency inversion principle, this time according to the implementation class to write the interface, this is not right, should first design abstraction, and then according to the abstract to implement, should be interface-oriented programming.

As we said above, the dependency inversion principle is not used in the traditional three-tier architecture, so what if the dependency inversion principle is applied to the traditional three-tier architecture? We know that in the traditional three-tier architecture, the UI layer directly depends on the BLL layer, and the BLL layer directly depends on the DAL layer. Because each layer depends on the implementation of the next layer, when the lower layer changes, the upper layer will also change. At this time, the three-tier architecture can be redesigned according to the principle of dependency inversion.

There should be no direct dependencies among the UI, BLL and DAL layers, and they should all rely on interfaces. First of all, we should determine the interface, the DAL layer abstracts the IDAL interface, and the BLL layer abstracts the IBLL interface, so the UI layer depends on the IBLL interface, and BLL implements the IBLL interface. The BLL layer depends on the IDAL interface, and DAL implements the IDAL interface. As shown in the following figure:

We talked about the principle of dependency inversion above, so what is the purpose of the principle of dependence inversion?

With the principle of relying on inversion, we can make our architecture more stable, flexible, and better able to cope with changes in requirements. Compared to the variability of details, abstract things are stable. So an abstraction-based architecture is much more stable than a detail-based architecture.

In the traditional three-tier architecture, by adding only one interface layer, we achieve dependency inversion in order to reduce the coupling between layers. With such an interface layer, the three-tier architecture really realizes the idea of "high cohesion and low coupling".

The principle of dependency inversion is architectural, so how can it be implemented at the code level? Let's take a look at the control reversal.

III. Control reversal

IOC: abbreviation of Inversion of Control, a way of reversing flows, dependencies, and interfaces. It hands over the controller (creation, maintenance) of objects that are traditionally directly controlled by program code to a third party, and assembles and manages object components through a third party (IOC container).

The IOC container, also known as the dependency injection framework, is provided by a dependency injection framework that is mainly used to map dependencies and manage the creation and lifetime of objects. The IOC container is essentially an object, which usually registers all the classes in the program and parses it directly from the container when using this class.

IV. Dependency injection

Dependency injection (DI): an abbreviation for Dependency Injection. Dependency injection is a way to implement control inversion, and the purpose of dependency injection is to achieve control inversion.

Dependency injection is a tool or means to help us develop loosely coupled and maintainable programs.

There are several common ways of dependency injection:

Constructor injection.

Property injection.

Method injection.

Constructor injection is the most frequently used, followed by attribute injection.

Take a look at the following example: a father tells a story to his child. As long as he gives the father a book, he can tell a story to his child according to this book. Let's do it in the most traditional way, where no design principles or design patterns are used.

First define a Book class:

Namespace DipDemo1 {public class Book {public string GetContent () {return "once upon a time there was a mountain and a temple on the mountain.";}

Then define a Father class:

Using System;namespace DipDemo1 {public class Father {public void Read () {Book book = new Book (); Console.WriteLine ("Dad begins to tell stories to his children"); Console.WriteLine (book.GetContent ());}

Then call in the Main method:

Using System;namespace DipDemo1 {class Program {static void Main (string [] args) {Father father = new Father (); father.Read (); Console.ReadKey ();}

Let's take a look at the diagram:

We see that Father is directly dependent on the Book class.

At this time, the demand has changed, do not give the father the book, give the father the newspaper, let the father read the newspaper to the child according to the newspaper, what should be done at this time? In the traditional way, we need to define a newspaper class at this time:

Namespace DipDemo1 {public class NewsPaper {public string GetContent () {return News;}

The dependency changes, because dad depends on the newspaper, which leads to the modification of the Father class:

Using System;namespace DipDemo1 {public class Father {public void Read () {/ / Reading / / Book book = new Book (); / / Console.WriteLine ("Dad begins to tell stories to his children"); / / Console.WriteLine (book.GetContent ()); / / newspaper NewsPaper paper = new NewsPaper () Console.WriteLine ("Dad begins to tell the news to the kids"); Console.WriteLine (paper.GetContent ());}

Suppose the demand changes again, and no more newspapers, but magazines, tablets, and so on. The demand is constantly changing, no matter how it changes, for Dad, he has been reading, but the specific reading material will change, which is the details, that is, the details will change. But abstraction doesn't change. If the traditional OOP idea is still used to solve the problem at this time, it will cause the program to be constantly modified. The following uses factory mode to optimize:

First, create an interface:

Namespace DipDemo2 {public interface IReader {string GetContent ();}}

Then let both the Book class and the NewsPaper class inherit from the IReader interface, the Book class

Namespace DipDemo2 {public class Book: IReader {public string GetContent () {return "once upon a time there was a mountain and a temple on the mountain.";}

NewsPaper class:

Namespace DipDemo2 {public class NewsPaper: IReader {public string GetContent () {return "Wang Congcong is restricted to high consumption."

Then create a factory class:

Namespace DipDemo2 {public static class ReaderFactory {public static IReader GetReader (string readerType) {if (string.IsNullOrEmpty (readerType)) {return null;} switch (readerType) {case "NewsPaper": return new NewsPaper () Case "Book": return new Book (); default: return null;}

The return value of the method inside is an interface type. Finally, call the factory class in the Father class:

Using System;namespace DipDemo2 {public class Father {private IReader Reader {get; set;} public Father (string readerName) {/ / here depends on abstract Reader = ReaderFactory.GetReader (readerName);} public void Read () {Console.WriteLine ("Dad begins to tell stories to his children") Console.WriteLine (Reader.GetContent ());}

Finally, call in the Main method:

Using System;namespace DipDemo2 {class Program {static void Main (string [] args) {Father father = new Father ("Book"); father.Read (); Console.ReadKey ();}

At this point, we can look at the dependency diagram:

At this point, Father does not have any dependence on Book and Paper, Father depends on the IReader interface, but also on the factory class, which in turn depends on the Book and Paper classes. The control reversal has actually been achieved here. Father (high-level) does not rely on lower-level (Book, Paper) but on IReader (abstraction), and concrete implementations are not created by high-level, but by third parties (in this case, factory classes). But here only uses the factory pattern to simulate the control inversion, but does not implement the dependency injection, the dependency still needs to request from the factory.

Let's go on to optimize the code, where you only need to modify the Father class:

The argument to using System;namespace DipDemo3 {public class Father {public IReader Reader {get; set;} / constructor is the IReader interface type / public Father (IReader reader) {Reader = reader } public void Read () {Console.WriteLine ("Dad begins to tell stories to his children"); Console.WriteLine (Reader.GetContent ());}

Call in the Main method:

Using System;namespace DipDemo3 {class Program {static void Main (string [] args) {var f = new Father (new Book ()); f.Read (); Console.ReadKey ();}

If you change it to Paper later, you need to modify the code:

Using System;namespace DipDemo3 {class Program {static void Main (string [] args) {/ / Book / / var f = new Father (new Book ()); / / f.Read (); / / Paprer var f = new Father (new Paper ()); f.Read (); Console.ReadKey () }}}

Since there is no factory here, we still need to instantiate the specific implementation class in the code. If we had an IOC container, we would not need to new an instance ourselves. Instead, the container would create an instance for us and inject dependent objects into it after creation.

Let's take a look at the dependency diagram:

Next, we use the Unity container to continue to optimize the above code. First, you need to install Unity in the project and search directly in NuGet:

Here, you only need to modify the Main method call:

Using System;using Unity;namespace UnityDemo {class Program {static void Main (string [] args) {/ / create container var container = new UnityContainer (); / / scan assemblies, configuration files / / register interfaces and implementation classes in the container, and create dependency container.RegisterType () / / register Father container.RegisterType () in the container; / / take out the class to be used from the container, and the container will create its own father pair / / will also get the object it depends on from the container and inject / / var father = container.Resolve () / / call the method father.Read (); Console.ReadKey ();} these are all the contents of the article "sample Analysis of dependency inversion principle DIP in C #". Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report