一、依赖(Dependency)
所有的工程产品都是有各个不同功能和特性的部件整合,通过相互协作完成来实现整个产品所需要实现的操作,各个不同相互协作关系形成了它们之间的相互依赖关系。对于软件开发角度从大的方面来说首先需要依赖于行业巨头提供的系统、开发工具、数据库等,中的方面需要依赖于行业巨头或第三方提供的框架、中间件等,而开发者所作的绝大部分工作是通过不同对类的定义整合这些软件、、框架、组件、中间件而形成自己的软件产品,而在整个过程中属性开发者原创的工作基本很少,这是软件行业的基本逻辑,无可厚非。但是绝大部分开发者所做的工作与大型电子产品组装厂里的流水线上的工人在本质上只有五十步与一百步差距,更加可怕的是我们绝大部分的建筑是建设在可能对我们产生敌意和威胁的国家的土地上。难道我们总是那么的功利搜索平台、电商平台国内发展的红红火火(这不是极其愉快、主动的向极有可能成为抢劫犯的团伙中成批量的输送人质吗?且在事后还感到很香很享受,像男人的名门一样被别人牢牢的攥在手中,虽然最近在特殊行业的特殊软件,被别人抓了一下,但整个在环境没有切肤之痛,在这种社会环境下,我不知道国内的软件行业还能享受到什么时候。),在国外电商平台发展同样红火,而决定整个行业基础和命运的开源NuGet平台依然是他们最先建立,最先使用并且在日益壮大着,他们真正做到的两手抓两手都要硬。在国内在基础上存在着巨大差距,对国外基础软件有着巨大依赖的情况下国内却没有一个像NuGet平台一样的开源网站,退一步来说把NuGet平台做一些专业翻译也是在软件基础上做了一些不那么功利且对后续发展绝对有益的事。
第一种情况的依赖是在程序开发最为直接和常见的表现是开发者自定义的存在着相互协作关系的且有各种功能和特性的类。第二种情况的依赖是声明在类或接口定义头部的关于系统的各种引用,还有嵌入到程序中由开发环境、框架和第三方提供的中间件,最后一种情况的依赖是是最没有存在感,作为软件开发基础的框架却从一开始就决定着开发者必须遵守它所规定的格式、规范。甚至开发思想和使用的相关技术有的时候也有框架所决定。就如前两章所述软件是一个工程产品,它必须存在着相互协作关系,也必须会构成依赖关系,最终在实现上形成耦合关系,使用先进的开发思想和技术通过转移依赖和耦合的对象,减少依赖和耦合的接触点,来达到降低耦合的目标。在同一个程序中先进的开发思想和技术只能转移动耦合关系,而不能减少或降低耦合关系,它们所能减少或降低的只是关系中的接触点。
二、依赖倒置原则(DIP-Dependence Inversion Principle)
依赖“倒”置是相对于依赖“正”置来说的,在程序开发中常见依赖“正”置现象是通过“new”关键字来实例类来调用或使用指定实例,即先实现或实物,在依赖实现或实物实例需要的操作。依赖“倒”置则是接口结合泛型等技术,通过“new”关键字实例化继承于指定接口的所有的类,即从依赖实现或实物转移到依赖规范和信息,在实现世界中也是存在的如法律,如果一个人做了件人神共愤的事,没有相应的法律条文就法官判他是无罪的。依赖倒置的本质原则就是 :通过抽象(接口或抽象类)使各个类或模块实现彼此独立,互不影响,实现模块间的松耦合。
在程序中实践依赖倒置原则时,通过“new”关键字实例化接口或抽象类(直接依赖从具体实现类转移),接口或抽象类再结合泛型技术来调用具体实现类(从直接依赖变为简接依赖)。如一个类调用其它类方法时,调用的不再是方法具体实现,只是方法一个声明信息,不能通过查看具体实现来认知该方法的具体功能,一个接口或抽象类可能有很多继承该接口或抽象类的实现类,所以在程序中实践依赖倒置原则时对接口或抽象类以及实现类中的方法进行详细而明确的注释,相对于依赖“正”置重要性显得更加突出,这样开发者才能更加准确、快速的使用接口或抽象类所声明的方法。如果在程序中实践依赖倒置原则时只重视方法的实现,轻视注释实质上在思想还是处于依赖“正”置阶段,所以在依赖“倒”置时详细而明确的注释可能与所注释的方法的声明或实现同样重要,有时甚至更为重要。
三、IoC容器、控制反转(IoC-Invertion of Control)和依赖注入(DI- Dependency Injection)
1、IoC容器的整体理解
容器:字面上理解就是装东西的东西。常见的变量、对象属性、实例等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,现在我们讨论的是这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调(闭包),通过这种容器,我们得以实现许多高级的功能,其中就有上述所提到的 “解耦”、“依赖注入”等。
2、IoC容器的内在理解
图3
从上图可以得到开发者通过自定义的接口结合泛型来实例化继承于该接口的类,整个过程是由开发者自己定义和实现的,对于开发者说具体实现的内存机制是处于白盒状态,清晰而明确。如果使用.NetCore框架中的默认IOC容器实现实例化操作则是调用集成在框架或中间件的相应方法实现的,相应方法通过接口来实例化继承于该接口的所有类,IOC容器是由软件巨头或有实力的第三方所提供的,整个过程是被.NetCore框架或中间件封装过的,对于开发者说具体实现的内存机制是处于黑盒状态的(示例程序:21-04-11_IOCDefaultDIPattern(001_通过默认ICO容器和自定义实现类的实例化操作_Web) https://download.csdn.net/download/zhoujian_911/16635870)。通过上述可以得到IOC容器的特性有:
(1)、实例化操作由开发者自己定义控制,转移的到由软件巨头或有实力的第三方所提供框架或中间件所控制,即由依赖于开发者自己转移到依赖由软件巨头或有实力的第三方所提供的框架或中间件。实际上.NetCore框架等的出现的一个重要原因就是微软为了实践依赖注入原则而开发的,并在框架中集成一个默认的IOC容器,来协助和简化整个操作过程,从而使用开发者更加快速的上手,但封装也越来赿严密,使开发者更加只知其然不知其所以然(从.NetCore1到.NetCore3.X或.Net5的发展中可以很明显的看出这中趋势。)。
(2)、IOC容器通过调用集成在容器中的相应方法来定义接口和相应实现类的映射关系以及类实例的生命周期(到此是显式的),从而构建映射依赖并管理类实例的创建和生命周期(到此是隐式、黑盒的,是由集成在框架或中间件中IOC容器,自动完成的)。
(3)、IOC容器对接口和相应实例构建映射最有可能的手段和方法有:由XML文件来定义;由泛型来定义;由反射来定义;至于实现可以从.NetCore框架的源码查看(https://github.com/dotnet)。
3、控制反转(IoC-Invertion of Control)
一种软件设计原则或思想,这种由思想是把开发者自己控制类型实例化操作,转移到由软件巨头或有实力的第三方所提供的框架或中间件。控制反转是从容器的角度对程序开发的整个过程进行描述,容器成为了整个程序开发过程的主体和中心而不是开发者自己。容器作为主体和中心的直接表现就是类在实例化之前必须使用容器所集成的方法来构建接口和实现类的映射关系,才能进行实例操作,否则在程序执行过程中会出现逻辑异常。
- 依赖注入(DI- Dependency Injection)
依赖注入是开发者把所需的操作对象预先注册到容器(实际上是实例化操作前的一些准备工作,只不过这些工作是不可见且由容器自动完成的),如果开发者没有把所需要对象在注册到容器,而直接调用中调在程序执行过程中就会出现逻辑异常。这些对象通过包含两种1、有特殊功能的中间件,2、通过调用集成在容器中的相应方法来定义接口和相应实现类的映射关系以及类实例的生命周期(到此是显式的),从而构建映射依赖并管理类实例的创建和生命周期。依赖注入实质上是向行程日志(容器)上写上行程(注册),完成整个行程日志上的所有行程(在程序执行时,在开始执行前先由容器自动执行这些准备工作)。
开发者有三种自定义方式来自定义依赖注入(容器不参与其中),它们分别是:接口注入、属性注入、与构造注入,其中最常见、最常用和相对效率较高的是构造注入。
最常用的依赖注入实例化操作方式有:泛型模板注入实例化,反射模板注入实例化,开发者自定义和容器都可以通过这两种方式来实现依赖注入的实例化操作(示例程序:21-04-05_DefaultDIPattern(003_使用常规、泛型、反射等 方式实默认容器注入和管道的建立_Web转控件台) https://download.csdn.net/download/zhoujian_911/16621630)。