一步一步造个Ioc轮子目录

一步一步造个IoC轮子(一):IoC是什么
一步一步造个IoC轮子(二):详解泛型工厂
一步一步造个IoC轮子(三):构造基本的IoC容器

前言

.net core正式版前两天发布了,喜大普奔,借此机会,强行来写第一篇博客

第一次写博客,有点紧张,不知怎么才能装做经常写的样子(:

第一次,造个小轮子吧,IoC容器,借此完善自己的类库


DI,IoC什么的高大上的名字是什么意思

我不是老司机,开C#时间不太长,以下粗浅个人见解,说错了打脸要轻一点,毕竟是还想靠脸吃饭 (:

怕词不达意,用个示例来说明吧,老司机绕行

小黄是一家电商公司的主程,有一天老板说我们加个发货短信通知发短信给客户吧

于是小黄找了一个叫XSMS的短信通道供应商签约了

然后小黄写了开始码代码了

首先写一个XSMS的类

        public class XSMS
{
private string sign;
public XSMS(string key, string pwd)
{
sign = "什么都有的电商公司";
}
private string FormatTpl(string tpl, string msg, long phoneNumber, string client, string sign)
{
return tpl.Replace("$sign", sign).Replace("$msg", msg).Replace("$client", client);
}
public void Send(string msg, long phone, string client, string tpl)
{
//签名等等把短信发到XSMS公司的接口上
//XXX的复杂发送代码
Console.WriteLine("从XSMS发短信:" + FormatTpl(tpl, msg, phone, client, sign));
}
}

接着,开始写业务调用了

        public static void Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
while (true)
{
SendMsg2Client();
Thread.Sleep( * );
}
}
private static void SendMsg2Client()
{
var cs = GetClient(); if (cs != null && cs.Count > )
{
string tpl = "[$sign]$client您好,$msg";
var sms = new XSMS("", "");
foreach (var c in cs)
{
sms.Send("您订购的充气女友已发货,查收后请爱惜使用", c.Key, c.Value, tpl);
}
}
} private static void SetSend(long phone)
{
//写到数据库标记已经发了短信
} private static Dictionary<long, string> GetClient()
{
//从数据库里取出需要发短信的客户
return new Dictionary<long, string> { { , "群主" } };
}

好了,业务上线了,老板很满意

过了好大半个月,老板找到小黄:小黄啊,我朋友那里发个短信才4.5分一条,你签那个什么通道怎么要6分一条啊,我们改为他公司的接口吧,我给他公司的技术员QQ你,你跟他联系吧

小黄找到老板朋友公司的技术员,让他搞了一个接口用他公司的帐号去发短信,

好了,小黄又开始加班撸代码了

    public class FriendSMS
{
private string sign;
public FriendSMS(string key, string pwd)
{
sign = "什么都有的电商公司";
}
private string FormatTpl(string tpl, string msg, long phoneNumber, string client, string sign)
{
return tpl.Replace("$sign", sign).Replace("$msg", msg).Replace("$client", client);
}
public void Send(string msg, long phone, string client, string tpl)
{
//签名等等把短信发到老板朋友公司提供的接口上
//XXX的复杂发送代码
Console.WriteLine("从FriendSMS发短信:" + FormatTpl(tpl, msg, phone, client, sign));
}
}

撸完短信类的代码,再去修改业务类代码

        private static void SendMsg2Client()
{
var cs = GetClient(); if (cs != null && cs.Count > )
{
string tpl = "[$sign]$client您好,$msg";
string key = "";//从配置文件或数据库里取得
string pwd = "";//从配置文件或数据库里取得
var sms = new FriendSMS(key, pwd);//改用老板朋友那家公司的接口了
foreach (var c in cs)
{
sms.Send("您订购的充气女友已发货,查收后请爱惜使用", c.Key, c.Value, tpl);
}
}
}

好了,业务上线了,老板满意,就是小黄累了点,给测试为难了一下:你这业务代码都改了,又要我重新测试整个业务了,下班你要请我大保健啊

业务上线一个月,老板把小黄叫到办公室:小黄啊,你写那个短信怎么老是有客户说收不到呢,你写什么垃圾代码啊,这么不稳定

小黄辩道:老板,不关我事了,是你那朋友公司提供那接口老是不稳定,经常把我们的短信吃掉,我跟他们天天对数据,头都大了,我跟他们技术了解过,他们是用阿里大鱼的接口,他们是写了个代理接口给我们用,不如我们直接用阿里大鱼的接口吧,一样是4.5分一条短信,直接对接肯定稳定很多,不会受制于你朋友的公司了

老板:好吧,弄好了请你大保健

这次小黄学乖了,妈蛋这次换阿里大鱼又要被测试骂了,怎么样才能让业务代码尽量不用改呢,百度了一番,找群里的问了一番,小黄决定用一个简单的实现:工厂模式

小黄开始重构代码了,XXSMS类样子操作方法都是一样,我要抽象一个接口出来

    public interface ISMS
{
void Init(string key, string pwd);
void Send(string msg, long phone, string client, string tpl);
}

写阿里大鱼及修改之前两个通道的代码

    public abstract class BaseSMS : ISMS
{
public virtual void Init(string key, string pwd)
{
throw new NotImplementedException();
} public virtual void Send(string msg, long phone, string client, string tpl)
{
throw new NotImplementedException();
}
protected string FormatTpl(string tpl, string msg, long phoneNumber, string client, string sign)
{
return tpl.Replace("$sign", sign).Replace("$msg", msg).Replace("$client", client);
}
}
public class AlidayuSMS : BaseSMS
{
private string sign;
public override void Init(string key, string pwd)
{
sign = "什么都有的电商公司";
}
public override void Send(string msg, long phone, string client, string tpl)
{
//签名等等把短信发到XSMS公司的接口上
//XXX的复杂发送代码
Console.WriteLine("从阿里大鱼发短信:" + FormatTpl(tpl, msg, phone, client, sign));
}
}
public class XSMS : BaseSMS
{
private string sign;
public override void Init(string key, string pwd)
{
sign = "什么都有的电商公司";
}
public override void Send(string msg, long phone, string client, string tpl)
{
//签名等等把短信发到XSMS公司的接口上
//XXX的复杂发送代码
Console.WriteLine("从XSMS发短信:" + FormatTpl(tpl, msg, phone, client, sign));
}
}
public class FriendSMS : BaseSMS
{
private string sign;
public override void Init(string key, string pwd)
{
sign = "什么都有的电商公司";
}
public override void Send(string msg, long phone, string client, string tpl)
{
//签名等等把短信发到老板朋友公司提供的接口上
//XXX的复杂发送代码
Console.WriteLine("从FriendSMS发短信:" + FormatTpl(tpl, msg, phone, client, sign));
}
}

然后写个简单工厂

    public class SMSFactory
{
public static ISMS GetSMS(string type)
{
switch (type.Trim().ToLower())
{
case "xsms": return new XSMS();
case "friendsms": return new FriendSMS();
case "alidayusms": return new AlidayuSMS();
}
throw new Exception("不存在的短信通道:" + type + "");
}
}

修改业务类代码

        private static void SendMsg2Client()
{
var cs = GetClient(); if (cs != null && cs.Count > )
{
string tpl = "[$sign]$client您好,$msg";
string key = "";//从配置文件或数据库里取得
string pwd = "";//从配置文件或数据库里取得
string type = "alidayusms";//从配置文件或数据库里取得
var sms = SMSFactory.GetSMS(type);
sms.Init(key, pwd);
foreach (var c in cs)
{
sms.Send("您订购的充气女友已发货,查收后请爱惜使用", c.Key, c.Value, tpl);
}
}
}

上线了,很稳定而且也便宜,老板很满意,给了50块小黄去大保健,小黄把这个大保健的机会让给了测试,因为测试这次怨气大点大,业务代码要测试,几个原来的通道接口也要测试

然后有一天,阿里那边通知:开放平台接口要改为HTTPS,小黄这听到这个消息,冷笑一声,嘿嘿不就改一下这个类而已么

然后小黄把阿里大鱼的类修改一下然后准备上线了,测试听到这个消息:妈蛋你当我透明了啊,跳过我就上线啦

小黄辩称道:就改了一下阿里大鱼那个类而已

测试:不行,你那个工厂依赖大鱼,重新编译了生成的DLL都不知跟原来业务有没有问题

小黄暗暗叫苦,这不是变着法子骗大保健么

让测试再坑了一次大保健,小黄到群里吐苦水,说测试为难自己,众群员也跟着谴责测试,

不过其中一个老司机说:这是你自找的,怪不了测试,你应该让业务代码与这些短信模块解耦,不要依赖这些模块

小黄:老司机,请教怎么破啊

老司机:依赖倒置,让这业务及你这个"工厂"与各个SMS类完全无关,一个SMS类起一个项目生成一个DLL以后你改类就改那个项目,加类就加个新项目,那个模块项目整个好交给测试就行了

小黄:那这个工厂也是要重新编译啊,测试到时也一样会找我麻烦

老司机:把你的工厂升级一下,做成反射工厂或者更一步到位用Ioc容器,需要什么样的短信模块,交给升级的工厂或Ioc容器来完成,这样一来,工厂或IoC容器是完全不用变化,业务代码更是完全不用动,以后测试也轻松了,分分钟反过来请你去大保健

小黄及众群员:嘀,学生卡

在这个依赖倒置的思想指导下,小黄找了一个IoC容器,把SMS模块写到Ioc容器的配置文件里

然后最后一次修改业务代码

        private static void SendMsg2Client()
{
var cs = GetClient(); if (cs != null && cs.Count > )
{
string tpl = "[$sign]$client您好,$msg";
string key = "";//从配置文件或数据库里取得
string pwd = "";//从配置文件或数据库里取得 var ctx = new IocContainer("cfg.xml");
var sms = ctx.Resolve<ISMS>();//从Ioc容器取出实现类
sms.Init(key, pwd);
foreach (var c in cs)
{
sms.Send("您订购的充气女友已发货,查收后请爱惜使用", c.Key, c.Value, tpl);
}
}
}

小黄修改了代码,跟测试保证以后不用动业务那些代码,以后测试只测试一下实现模块就OK了,上线运行后,测试邪恶的笑了一下捡起了地上的肥皂 (逃

好了,经过上面纯属虚构的故事,相信大家都对工厂,Ioc和DI等有点理解了

DI (Dependency Injection,依赖注入)

一看这名字不百度都不知是什么意思,那我们再先看一下相关的概念吧

依赖倒置原则(Dependence Inversion Principle,简称DIP)

该死,又多一个概念,引入这个概念的话看来又要引入另一个概念了:解耦

什么是解耦,解耦字面的意思也很明白了解除耦合,解除软件各个模块之间的耦合,让一个模块不要依赖另一个模块

在上面的案例里,解耦就是解除了业务代码对各个SMS模块的耦,业务跟具体实现的模块完全没有依赖,第一版中,业务是编码阶段就跟各SMS类耦合了,严重依赖SMS类,依赖在编码阶段

抽象出接口,然后使用了IoC后这个依赖在编码阶段是没有的,依赖调用实现的SMS的发生在系统运行的阶段,依赖在运行时,IoC把这种依赖完全反转过来了,耦合解除了,依赖倒置了

IoC (Inversion of Control,控制反转)

Ioc的概念也就跟依赖倒置差不多,是控制权的转移,原先由业务代码决定用那个SMS模块的转移到由Ioc容器根据配置文件决定使用什么SMS模块,控制权转移到IoC框架上.

Ioc思想实现的Ioc容器就是负责按配置文件注入依赖(DI)的模块到容器里返回给业务调用

简单说,IoC容器是就一个豪华版工厂,自动化装配的工厂,工厂知道是什么吧,就是我把产品规格发给你,你把产品给我,我不管你给的是怎么制作怎么实现的产品,用什么材料我一概不管,我只要合我规格的就行。

IoC就是这样的一个工厂,我们在业务代码里调用IoC容器(工厂),把需求(产品规格)发给IoC容器,容器返回实现类(产品)给业务,业务就可以按这个规格来草作这个产品了

IoC容器就是负责注入依赖的模块

粗浅见解,打脸轻点


造轮子目标

环境当然是.net core下了

支持配置文件

支持注册单例

支持延迟加载

我不是标题党,用.net魔法真的可以是直接new的速度,当然这个多了一层调用,肯定会下降一丁点,但速度比反射型的不知高那里去了


 待续,不会太监,我不是给大鱼打广告

一步一步造个IoC轮子(一):IoC是什么的更多相关文章

  1. 一步一步造个IoC轮子(二),详解泛型工厂

    一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):Ioc是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 详解泛型工厂 既然我说IoC容器 ...

  2. 一步一步造个IoC轮子(三):构造基本的IoC容器

    一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):Ioc是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 定义容器 首先,我们来画个大饼, ...

  3. 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

    阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...

  4. (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性

    转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...

  5. 一步一步深入spring(2)-三种方式来实例化bean

    在一步一步深入spring(1)--搭建和测试spring的开发环境中提到了一种实例化bean的方式,也是最基本的使用构造器实例化bean 1.使用构造器实例化bean:这是最简单的方式,Spring ...

  6. 一步一步构建手机WebApp开发——页面布局篇

    继上一篇:一步一步构建手机WebApp开发——环境搭建篇过后,我相信很多朋友都想看看实战案例,这一次的教程是页面布局篇,先上图: 如上图所示,此篇教程便是教初学者如何快速布局这样的页面.废话少说,直接 ...

  7. 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

    之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...

  8. 通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

    目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实现一个简单的基于.net的微服务电商系统(二)--通讯框架讲解 三.通过Dapr实现一个简单的基于.net的微服务电 ...

  9. 一步一步学ROP之gadgets和2free篇(蒸米spark)

    目录 一步一步学ROP之gadgets和2free篇(蒸米spark) 0x00序 0x01 通用 gadgets part2 0x02 利用mmap执行任意shellcode 0x03 堆漏洞利用之 ...

随机推荐

  1. java中的subString具体解释及应用

    substring(參数)是java中截取字符串的一个方法 有两种传參方式 一种是public String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个 ...

  2. ant脚本中设置环境变量

    http://blog.csdn.net/quqi99/article/details/5329841

  3. Mysql存储过程中使用cursor

    一.表 学生表 CREATE TABLE `t_student` (    `stuNum` int(11) NOT NULL auto_increment,    `stuName` varchar ...

  4. [NPM] Pipe data from one npm script to another

    In an effort to bypass saving temporary build files you can leverage piping and output redirection t ...

  5. 数据序列化之protobuf

    数据序列化之protobuf 很多时候需要将一些数据打包,就是把这些数据搞在一起,方便处理.最常见的情况就是把需要传输的数据,当然数据不止一条,打包成一个消息,然后发送出去,接收端再以一定的规则接收并 ...

  6. 小强的HTML5移动开发之路(35)——jQuery中的过滤器详解

    1.基本过滤选择器 :first :last :not(selector) :selector匹配的节点之外的节点 :even :偶数 :odd :奇数 :eq(index) :gt(index) : ...

  7. WPF+SignalR实现用户列表实时刷新

    原文:WPF+SignalR实现用户列表实时刷新 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/lordwish/article/details/5 ...

  8. 要求两个异步任务都完成后, 才能回到主线程:dispatch_group_t

    需求:两个异步任务都完成后, 回到主线程 /** 1.下载图片1和图片2 2.将图片1和图片2合并成一张图片后显示到imageView上 思考: * 下载图片 : 子线程 * 等2张图片都下载完毕后, ...

  9. Zygote总结

    Zygote是由init进程通过解析 init.zygote.rc 文件而创建的,zygote所对应的可执行程序 app_process,所对应的源文件是App_main.cpp,进程名为zygote ...

  10. 【BZOJ 1025】[SCOI2009]游戏

    [题目链接]:http://www.lydsy.com/JudgeOnline/problem.php?id=1025 [题意] [题解] 每一个对应关系,里面其实都会生成大小不一的几个环. 每一个环 ...