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的设计模式>和< 依赖注入模式>)从纯理论的角度 ...
随机推荐
- js模拟ctrl+c的问题
1.这种方式只可以对显示的textbox和textarea使用,对于display:none和visibility hidden 以及其他标签无效 var message = document.get ...
- Jenkins+PowerShell持续集成环境搭建(五)SSRS项目
部署SSRS项目的原理是使用ReportingService2005类的方法实现rdl文件上传等功能. 上传rdl文件的脚本示例: $username="Victor"; $pas ...
- easy install 与pip
easy_insall的作用和perl中的cpan, ruby中的gem类似,都提供了在线一键安装模块的傻瓜方便方式,而pip是easy_install的改进版, 提供更好的提示信息,删除packag ...
- 配置 BizTalk Server
使用“基本配置”或“自定义配置”配置 BizTalk Server. 基本配置与自定义配置 如果配置使用域组,则进行“自定义配置”. 如果配置使用自定义组名称而不是默认组名称,则进行“自定 ...
- JarvisOJ BASIC 德军的密码
已知将一个flag以一种加密形式为使用密钥进行加密,使用密钥WELCOMETOCFF加密后密文为 000000000000000000000000000000000000000000000000000 ...
- POJ 1017 最少包裹
参考自:https://www.cnblogs.com/ECJTUACM-873284962/p/6414760.html Packets Time Limit: 1000MS Memory Li ...
- 洛谷P1781宇宙总统题解
题目 此题原本是一个简单的排序,但因为数据范围的限制,所以变成了一个需采用字符串排序的题目,接下来我将给大家讲一下如何字符串排序. 首先先判断为位数上是否相同,如果不同再比较那一位的数就可以了 #in ...
- POJ3417 Network(算竞进阶习题)
LCA + 树上差分(边差分) 由题目意思知,所有主要边即为该无向图的一个生成树. 我们考虑点(u,v)若连上一条附加边,那么我们切断(u,v)之间的主要边之后,由于附加边的存在,(u,v)之间的路径 ...
- Git的搭建
Git的搭建 第1步:官网下载安装git 第2步:github官网注册账号 第3步:配置git 第4步:github这是秘钥 第5步:上传本地工程到git 主要参考的博客(这三篇博客能让你顺利上传至g ...
- codeforces1096G Lucky Tickets
题目链接:https://codeforces.com/problemset/problem/1096/G 大意:给出\(k\)个数码\(d_1,d_2,\cdots,d_k\),构造一个由这\(k\ ...