C#设计模式 —— 依赖注入
在说依赖注入之前,先了解下什么是接口。 接口的相关规则:
1. 接口是一个引用类型,通过接口可以实现多重继承。
2. C#中接口的成员不能有new、public、protected、internal、private等修饰符。
3. 接口中只能声明”抽象”成员,所以不能直接下一步对接口进行实例化(即不能使用 new 操作符声明一个接口的实例对 象),而不能声明共有的域或者私有的成员变量。
4. 接口声明不包括数据成员,只能包含方法、属性、事件、索引等成员。
5. 接口名称一般都以“I”作为首字母(当然不这样声明也可以),这也是接口和类的一个区别之一。
6. 接口成员的访问级别是默认的(默认为public),所以在声明时不能再为接口成员指定任何访问修饰符,否则 编译器会报错。
7. 接口成员不能有static、abstract、override、virtual修饰符,使用new修饰符不会报错,但会给出警告说不需要关键字new。
8. 在声明接口成员的时候,不准为接口成员编写具体的可执行代码,也就是说,只要在对接口进行声明时指明接口的成员名称和参数就可以了。
9. 接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类(通过具体的可执行代码实现接口抽象成员的操作)。
很多时候看到这么多的概念,也是云里雾里的。项目中的接口使用也是按照老代码依葫芦画瓢。如果是自己练手的代码或者demo,也是没有使用接口。(给自己的借口就是,我只是做些小的东西,根本就不需要使用接口一样可以跑很溜啊。)
接口是什么?(说说我自己的理解,不一定对)
接口就是为了更换一个可能过时或者错误的实现而准备的。就像我们的电脑,里面就到处都是接口。usb、内存条、硬盘、电池、键盘…等等都是有各自的接口。我们可以通过硬盘接口换个更大的硬盘或者换个更快的固态硬盘。如果键盘坏了,也可以通过键盘接口买个新的键盘换上去。这就是接口明显的好处。接口也可以理解成大家的约定。约定了特定接口的大小功能等等。
那么我们写代码也是一样,在某些地方可能会经常变动,逻辑会经常修改的地方使用接口约定。下面我们就用硬盘的接口来做示例吧。
首先定义一个硬盘接口。(一个name属性,一个读一个写的方法)
// 硬盘接口
interface IHardDisk
{
string name { get; } //硬盘的名字属性
void read(); //读取数据方法
void write(string str); //写数据
}
然后我们买了一个200G的硬盘,它实现了上面的接口。
public class HardDisk200 : IHardDisk
{
public string name
{
get { return '我是200G硬盘'; }
}
public void read()
{
Console.WriteLine('我可以写入数据哦....');
}
public void write(string str)
{
Console.WriteLine(str);
}
}
在电脑中使用这个硬盘。
static void Main(string[] args)
{
//这里的h就是一个插在接口上的设备
IHardDisk h = new HardDisk200();
h.read();
h.write(h.name + ',我可以写入数据哦');
Console.ReadKey();
}

某天,我们发现这个硬盘太小了,需要换个1T的。(那样我们可以存很多很多的电影>_),那么买吧。
public class HardDisk1T : IHardDisk
{
public string name
{
get { return '我是1T硬盘'; }
}
public void read()
{
Console.WriteLine('我可以写入数据哦....');
}
public void write(string str)
{
Console.WriteLine(str);
}
}
然后怎么使用了?只要在电脑上的接口直接插上新的硬盘就ok了,其他的什么地方都不用改。

这就是使用接口的好处。当某天我们发现电脑太慢了,我们可以买个固态硬盘,直接在接口使用的地方换上就可以了,其他地方完全不用修改。

这样,我们就可以在不同时期或不同情况下灵活更换继承实现了接口的任何对象,而不用修改其它地方的代码。
又或者说,实现了这个接口的设备就是存储设备。(它一定有存也一定可以储,也就是一定可以写入和读出数据。)
在我们了解了什么是接口之后,我们接着来说说今天主要的主题吧。
还是先从例子入手,且是我们学过编程都知道的例子,三层。(什么?你不知道什么是三层?那你别看了,先补习了再过来)
我们先来写个简单的三层伪代码。
//DAL:
public class DALMsSqlHelper
{
public int add(string str)
{
// 省略具体实现
return ;
}
// 省略具体实现,如修改 删除 查询
} //BLL:
public class BLLAddStudent
{
DALMsSqlHelper mssql = null;
public BLLAddStudent()
{
mssql = new DALMsSqlHelper();
}
public int addStudent()
{
string str = ''; //...省略具体实现
return mssql.add(str);
}
} //UI:
public class UI
{
BLLAddStudent s = new BLLAddStudent();
public UI()
{
s.addStudent();
}
}
应该说简单得不能在简单的三层。
就在系统用了一年之后,老板说:”听说oracle很牛逼,大公司都是用的oracle。咱们也换上吧。“。 好,那就换吧。
//DAL:
public class DALOracleSqlHelper
{
public int addOracle(string str)
{
// 省略具体实现
return ;
}
// 省略具体实现,如修改 删除 查询
} 显然BLL也要进行修改,因为BLL引用了DAL的查询类。 //BLL:
public class BLLAddStudent
{
DALOracleSqlHelper mssql = null;
public BLLAddStudent()
{
mssql = new DALOracleSqlHelper();
}
public int addStudent()
{
string str = '';
// 省略具体实现
return mssql.addOracle(str);
}
}
不就换个数据库吗?为何修改这么大,要是老板哪天又要换回oracle怎么办?这得好好想个办法。
首先,我们定义一个数据访问的接口。
public interface ISqlHelper
{
int add();
// 省略具体实现,如修改 删除 查询
} //BAL修改如下:
public class DALMsSqlHelper : ISqlHelper
{
public int add(string str)
{
// 省略具体实现
return ;
}
// 省略具体实现,如修改 删除 查询
}
public class DALOracleSqlHelper : ISqlHelper
{
public int addOracle(string str)
{
// 省略具体实现
return ;
}
// 省略具体实现,如修改 删除 查询
public int add(string str)
{
// 省略具体实现
return ;
}
} //BLL:
public class BLLAddStudent
{
ISqlHelper mssql = null;
public BLLAddStudent(ISqlHelper sqlhelper)
{
mssql = sqlhelper;
}
public int addStudent()
{
string str = ''; //...省略具体实现
return mssql.add(str);
}
} //UI:
public class UI
{
public UI()
{
ISqlHelper sqlhelper = new DALOracleSqlHelper();
BLLAddStudent s = new BLLAddStudent(sqlhelper);
s.addStudent();
}
}
如果哪天老板又要换会mssql怎样办。那么仅仅只要修改UI

又过一年之后,因为公司不景气。所以又来需求了。老板:”唉,算了。我们还是用mysql吧。免费的,为公司节省点“。那么我们又要修改了。
首先需要重新写个mysql的实现。
//DAL:
public class DALMySqlHelper : ISqlHelper
{
public int add(string str)
{
// 省略具体实现
return ;
}
// 省略具体实现,如修改 删除 查询
} //UI实现如下:
public class UI
{
public UI()
{
ISqlHelper sqlhelper = new DALMySqlHelper();
BLLAddStudent s = new BLLAddStudent(sqlhelper);
s.addStudent();
}
}
我们有没有发现。我们只是在DAL新增了一个mysql的实现和修改了下UI层的接口构造。其中BLL我们根本就没有动它的。
是的,这样我们就可以说这里的UI对于BLL来说就是”依赖注入“,BLL对于UI来说就是”控制反转“。所以,我觉得依赖注入和控制反转是同一个概念,只是立场不同。
上面,我们看到了虽然BLL层已经不需要变动就可以新增一个数据源的访问。那么我们能不能也不修改UI层呢? 这里就可以用到反射了。

然后,不管老板想怎么折腾,我只需要改改配置文件就可以了。甚至都不用动代码。(如果需要新增一个数据源操作,也只要重新实现下,然后改改配置文件)。
C#设计模式 —— 依赖注入的更多相关文章
- Magento2 API 服务合同设计模式 依赖注入 介绍
公共接口和API 什么是公共界面? 一个公共接口是一组代码,第三方开发者可以调用,实现或构建一个 插件 .Magento保证在没有重大版本更改的情况下,此代码在后续版本中不会更改. 模块的公共接口 标 ...
- php设计模式-依赖注入模式(Dependency Injection)
依赖注入模式用来减少程序间的耦合.当一个类要使用另一个类时,一般的写法如下: <?php class Test1 { public function say() { echo 'hello'; ...
- 依赖注入[2]: 基于IoC的设计模式
正如我们在<控制反转>提到过的,很多人将IoC理解为一种"面向对象的设计模式",实际上IoC自身不仅与面向对象没有必然的联系,它也算不上是一种设计模式.一般来讲,设计模 ...
- 依赖注入 – ASP.NET MVC 4 系列
从 ASP.NET MVC 3.0 开始就引入了一个新概念:依赖解析器(dependence resolver).极大的增强了应用程序参与依赖注入的能力,更好的在 MVC 使用的服务和创 ...
- 依赖注入[8]: .NET Core DI框架[服务消费]
包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IServiceProvider对象.当需要消费某个服务实例的时候,我们只需要指定服务类型调用IServicePr ...
- 依赖注入[7]: .NET Core DI框架[服务注册]
包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IServiceProvider对象.服务注册就是创建出现相应的ServiceDescriptor对象并将其添加到 ...
- 依赖注入[6]: .NET Core DI框架[编程体验]
毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动时构建请求处理管道过程中,以及利用该管道处理每个请求过程中使用到的服务对象均来源于DI容器.该DI容器不仅为A ...
- 依赖注入[5]: 创建一个简易版的DI框架[下篇]
为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...
- 依赖注入[4]: 创建一个简易版的DI框架[上篇]
本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度 ...
随机推荐
- Nginx 反向代理接收用户包体方式
陶辉91课 如果proxy_request_buffering 设置为on的时候是等待nginx读取完包体后再发送上游服务器 一般依赖于nginx处理能力 client_body_in_file_o ...
- SpringMVC 复杂对象数据绑定
表单在 web 页面上无处不在,有些表单可能很复杂,大部分表单里的输入项都会对应后端对象属性.SpringMVC 可以自动将表单值绑定到对象上!而且能绑定很复杂的对象!!这里就不写那些基本的表单绑定了 ...
- Fence Repair POJ - 3253 哈夫曼思想 优先队列
题意:给出一段无限长的棍子,切一刀需要的代价是棍子的总长,例如21切一刀 变成什么长度 都是代价21 列如7切成5 和2 也是代价7题解:可以利用霍夫曼编码的思想 短的棍子就放在底层 长的尽量切少一次 ...
- [PA2012] Tax
传送门:>Here< 题意:给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价.起点的代价是离开起点的边的边权,终点的代价是 ...
- SSL加速卡调研的原因及背景
SSL加速卡调研的原因及背景 SSL加速卡调研的原因及背景 网络信息安全已经成为电子商务和网络信息业发展的一个瓶颈,安全套接层(SSL)协议能较好地解决安全处理问题,而SSL加速器有效地提高了网络安全 ...
- 【HDU5950】Recursive sequence(矩阵快速幂)
BUPT2017 wintertraining(15) #6F 题意 \(f(1)=a,f(2)=b,f(i)=2*(f(i-2)+f(i-1)+i^4)\) 给定n,a,b ,\(N,a,b < ...
- Linux 检查端口gps命令
由于是游戏业务,环境主要是Nginx+Tomcat+Java Program gps脚本环境以及效果图如下: #!/bin/bash function Printf (){ == ];then pri ...
- SPOJ divcntk(min25筛)
题意 \(\sigma_0(i)\) 表示 \(i\) 的约数个数 求 \[ S_k(n)=\sum_{i=1}^n\sigma_0(i^k)\pmod {2^{64}} \] 共 \(T\) 组数据 ...
- Leetcode 349. 两个数组的交集 By Python
给定两个数组,编写一个函数来计算它们的交集. 示例 1: 输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2] 示例 2: 输入: nums1 = [4,9,5], ...
- python3 特性
切片: 就是可以取到对象中任意位置的元素,[start:end:interval]:字符串.列表.元组可切片,字典.set()不可: L[:] #复制原L L[:10] #前十个 L[-10:] #后 ...