.net core发布了,一步一步造个Ioc轮子,弄点.net魔法,近new的速度(一)

前言

.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(1000 * 60);
            }
        }
        private static void SendMsg2Client()
        {
            var cs = GetClient();

            if (cs != null && cs.Count > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                var sms = new XSMS("123", "666");
                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> { { 13666666666, "群主" } };
        }

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

过了好大半个月,老板找到小黄:小黄啊,我朋友那里发个短信才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 > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                string key = "123";//从配置文件或数据库里取得
                string pwd = "666";//从配置文件或数据库里取得
                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 > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                string key = "123";//从配置文件或数据库里取得
                string pwd = "666";//从配置文件或数据库里取得
                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 > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                string key = "123";//从配置文件或数据库里取得
                string pwd = "666";//从配置文件或数据库里取得

                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的速度,当然这个多了一层调用,肯定会下降一丁点,但速度比反射型的不知高那里去了



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

时间: 2024-07-30 23:54:06

.net core发布了,一步一步造个Ioc轮子,弄点.net魔法,近new的速度(一)的相关文章

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

一步一步造个Ioc轮子目录 .net core发布了,一步一步造个Ioc轮子,弄点.net魔法,近new的速度(一) 一步一步造个Ioc轮子(二),详解泛型工厂 详解泛型工厂 既然我说Ioc容器就是一个豪华版工厂,自动化装配的工厂,那我们就从工厂入手吧,先造个工厂,然后升级成Ioc容器 首先我们来写一个最最最简单的抽象工厂类,还是以前一篇的短信为例 public class SMSFactory { public static ISMS Get() { return new XSMS(); }

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

一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):Ioc是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 定义容器 首先,我们来画个大饼,定义好构造函数,注册函数及获取函数这几个最基本的使用方法 /// <summary> /// IoC容器 /// </summary> public class Container { /// <summary> /// 构造函数 /// </summary>

【新手出发】从搭虚拟机开始,一步一步在CentOS上跑起来.Net Core程序

文章背景 微软6月26号发布core 1.0版本后,园子里关于这方面的文章就更加火爆了,不管是从文章数量还是大家互动的热情来看,绝对是最热门的技术NO.1.我从去年底开始接触.net core到现在也大半年了,一直停留在浏览各种帖子上,偶尔新建个项目敲几行代码练习一下.可是对于core最大的卖点——跨平台,一直没法实际体验一回,因为压根没接触过Linux,完全不会那些命令,甚至虚拟机都没玩过?,想在Linux上实战操作一下可想有多困难.虽然园子里很多文章都有教程,但大神们一开始直接就上各种命令代

WCF 一步一步 发布 WCF服务 到 IIS (图)

WCF 一步一步 发布 WCF服务 到 IIS (图) 使用VS自带的WCFSVCHost(WCF服务主机)发布WCF服务,时刻开发人员测试使用. 下面我们来看一下如何在IIS中部发布一个WCF服务. 环境是VS 2008 (公司电脑没有安装VS2010)^_^ 我们从头开始,不写代码,完全的配置,会收获不小. 新建一个WCF 服务库 建立一个WCF服务应用程序 结果如下 删除掉WCF程序中不需要的默认文件,如下图   为WcfService1项目添加WcfServiceLibrary1的引用,

一步一步用jenkins,ansible,supervisor打造一个web构建发布系统

新blog地址:http://hengyunabc.github.io/deploy-system-build-with-jenkins-ansible-supervisor/ 一步一步用jenkins,ansible,supervisor打造一个web构建发布系统. 本来应该还有gitlab这一环节的,但是感觉加上,内容会增加很多.所以直接用github上的spring-mvc-showcase项目来做演示. https://github.com/spring-projects/spring-

一步一步学会系统发布

跟着牛腩老师做完发布系统,所有的结局都已写好,一场初雪,美的让我忘了还欠她一个美丽的转身--发布,但是小编呢,今天不以牛腩老师的新闻发布系统为例,以考试系统为例,跟小伙伴分享系统发布的点点滴滴.最近小编接手了一个高大上的任务,考试系统维护,用我小伙伴的话来吐槽一下就是:被考试系统折磨的不成人样了.维护工作是极大耐心的.从头到尾读着别人写的代码,复制别人的想法,做着自己的维护......是不是每个搞维护的都有要抽死coder的冲动"你丫写些什么,说好的注释代码2:1呢!" 说真心话,比珍

记录这两年是如何一步一步转型到.net core+k8s

2017年12月份,我离开北京,回到了武汉,开始在现在这家公司担任架构师工作.经过2年的时间,逐步完成以.net core+k8s为核心的技术架构.文末有彩蛋. 以下整理这两年的主要时间节点: 2018年1月到2018年3月 基于.net core的底层框架的封装,封装了数据访问,缓存,消息队列,加解密,日志,文档工具等一系列组成一个项目的必要组件.因为之前有沉淀,这一块的封装并没有花费太大的时间,主要是定标准和规范. 2018年4月到2018年6月 开发一系列公共服务,用户中心,认证授权服务,

ASP.NET Core 发布至Linux生产环境 Ubuntu 系统

ASP.NET Core 发布至Linux生产环境 Ubuntu 系统,之前跟大家讲解了 dotnet publish 发布,而没有将整个系统串起来. 今天就跟大家综合的讲一下ASP.NET Core发布至Linux生产环境. 开发及发布环境:WIN10 x64  生产Linux环境:Ubuntu 14.04 发布的示例代码: https://github.com/linezero/NETCoreBBS 代码下载下来,首先请注释 Program.cs 中 .UseUrls("http://*:8

Core 发布至Linux

ASP.NET Core 发布至Linux生产环境 Ubuntu 系统 ASP.NET Core 发布至Linux生产环境 Ubuntu 系统,之前跟大家讲解了 dotnet publish 发布,而没有将整个系统串起来. 今天就跟大家综合的讲一下ASP.NET Core发布至Linux生产环境. 开发及发布环境:WIN10 x64  生产Linux环境:Ubuntu 14.04 发布的示例代码: https://github.com/linezero/NETCoreBBS 代码下载下来,首先请