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的设计模式>和< 依赖注入模式>)从纯理论的角度 ...
随机推荐
- Military Problem CodeForces 1006E (dfs序)
J - Military Problem CodeForces - 1006E 就是一道dfs序的问题 给定一个树, 然后有q次询问. 每次给出u,k, 求以u为根的子树经过深搜的第k个儿子,如果一个 ...
- bzoj5358
Problem A. 口算训练Input file: stdinOutput file: stdoutTime limit: 5 secondsMemory limit: 512 megabytes小 ...
- mysql-语法大全
DDL语句 库 创建 create database 库名 charset utf8; 删除 drop database 库名; 修改 alter database 库名 charset latin; ...
- no module named 'win32api'问题
运行scrapy时,报错no module named 'win32api' 解决办法: https://github.com/mhammond/pywin32/releases 下载对于python ...
- #189 stat(动态规划)
容易想到固定第一个排列为1~n,算出答案后乘上n!即可,但这样就离正解走远了. 考虑排列dp的一般套路,将数从大到小加入排列,这样每个位置第一次填数时(不管是第一个还是第二个排列)其贡献就确定了. 显 ...
- POJ 2299 -Ultra-QuickSort-树状数组求逆序数
POJ 2299Ultra-QuickSort 使用树状数组记录逆序对数. 把数组按照大小顺序插入,getsum(i)就是i前面的比他大的数. #include <cstdio> #inc ...
- 进程间的通讯————IPC
""" IPC 指的是进程间通讯 之所以开启子进程 肯定需要它帮我们完成任务 很多情况下 需要将数据返回给父进程 然而 进程内存是物理隔离的 解决方案: 1.将共享数据放 ...
- [USACO18DEC]Balance Beam
题目链接:这里 或者这里 答案是很显然的,记\(g(i)\)为在\(i\)下平衡木时的期望收益 那么\(g(i)=max(f(i),\frac{g(i-1)+g(i+1)}{2})\) 好了做完了 T ...
- 华为Python 算法面试题
华为算法面试题 """ 算法题: 提供一个序列,完成对这个序列的分割.要求分割后的两个序列彼此差值最小 实现函数,返回两个序列 """ de ...
- MT【245】小概率事件
(2011年AAA测试)将一枚均匀的硬币连续抛掷$n$次,以$P_n$ 表示未出现连续3次正面的概率.求$\{P_n\}$.并讨论$\{P_n\}$单调性和极限. 分类讨论:第$n$次反面则未出现连续 ...