• autofac


    通过autofac教你彻底明白依赖解耦(一)理论知识

     

    先说讲到起茧的依赖反转(DI)原则

    此原则用来解耦合,使高层次的模块不依赖于低层次的模块
    这是啥意思呢?
    啥是高层次,啥是低层次?

    所谓高层次说白了就是抽象,在程序里面对应我们定义的接口,抽象类

    所谓低层次对应的是继承抽象类,实现接口的类型。

    当然高层低层也有软件结构层次的意思,其实这个高层的结构一样对于低层来说是很抽象的东西,可以用一样的理解方式来理解结构层次的依赖。

    啥是高层不依赖低层?
    举个栗子:
    就像领导总是抽象的,你们给我把事情做完,谁做(依赖接口),你怎么做(接口方法)哥哥我不管,我不依赖你的实现,哥只知道你有这能力(接口),那做实际事情的总是我们这些码农呗(实例化出来的对象),也就是低层。

    做个具体实现的栗子,销售部和开发部,销售部在卖东西需要据客户需求添加新功能的时候,他们老板总不能直接跟你技术部的码农直接对话吧?肯定是跟你技术部的老大直接交涉。这个就是高层抽象不依赖低层实现,抽象只依赖抽象。当然具体实现也只依赖抽象。

    复制代码
    public interface ISalesman
    {
        void AddNewFunction(ICodeFarmer manager);
    }
    
    public class SalesManager : ISalesman
    {
        public void AddNewFunction(ICodeFarmer manager)
        {
            manager.AddNewFunction();
        }
    }
    
    public interface ICodeFarmer
    {
        void AddNewFunction();
    }
    复制代码

    如上代码。应该一目了然了。

    那么其实标准的说法是:

    1. 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
    2. 抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。

    对于第2点,啥玩意呢?其实不用多解释,就是第一点的补充而已,抽象都依赖抽象了,你具体实现敢不依赖抽象?你销售部老板都只能找我技术部老板谈,你销售部小罗罗当然也只能和我技术部老大提问题!

    关于依赖注入

    我再写一个代码:还是上面的栗子,现在把客户的部分也加上来,全部的代码现在如下:

    复制代码
    public interface ISalesman
    {
        void AddNewFunction(ICodeFarmer manager);
    }
    
    public class SalesManager : ISalesman
    {
        public void AddNewFunction(ICodeFarmer manager)
        {
            manager.AddNewFunction();
        }
    }
    
    public interface ICodeFarmer
    {
        void AddNewFunction();
    }
    
    public class CodeFarmerManager : ICodeFarmer
    {
        public void AddNewFunction()
        {
            //todo
        }
    }
    
    public interface IClient
    {
        void AddNewFunction(ISalesman salesman);
    }
    
    public class ShaBClient : IClient
    {
        private ICodeFarmer CodeFarmerManager;
    
        public ShaBClient()
        {
            CodeFarmerManager = new CodeFarmerManager();
        }
    
        public void AddNewFunction(ISalesman salesman)
        {
            salesman.AddNewFunction(CodeFarmerManager);
        }
    }
    复制代码

    现在问题来了,我客户照道理说我只需要和你销售部的打交道,但是实际的代码里面为了调用销售部的方法,需要和搞代码的屌丝也要有依赖

    private ICodeFarmer CodeFarmerManager;

    客户表示很不开心。这就是第2个常见的错误,依赖了不该依赖的对象。我觉得这也是依赖中的一个常见的问题。

    好吧,怎么解决这个问题呢?

    简单,在SalesManager构造函数里面初始化一个ICodeFarmer类型就行了
    现在代码变成:

    复制代码
    public interface ISalesman
    {
        void AddNewFunction();
    }
    
    public class SalesManager : ISalesman
    {
        private ICodeFarmer CodeFarmerManager;
    
        public SalesManager()
        {
            CodeFarmerManager = new CodeFarmerManager();
        }
    
        public void AddNewFunction()
        {
            CodeFarmerManager.AddNewFunction();
        }
    }
    
    public interface ICodeFarmer
    {
        void AddNewFunction();
    }
    
    public class CodeFarmerManager : ICodeFarmer
    {
        public void AddNewFunction()
        {
            //todo
        }
    }
    
    public interface IClient
    {
        void AddNewFunction();
    }
    
    public class ShaBClient : IClient
    {
        public void AddNewFunction()
        {
            ISalesman salesman = new SalesManager();
            salesman.AddNewFunction();
        }
    }
    复制代码

    这样问题貌似解决了,但是!!!!!

    突然有一天,技术部的经理表示,哥哥我写代码都没时间,还要整天处理这些和外面打交道的事情,哥哥我不干了。这时公司没办法,只好又招聘了一个人,我们暂且叫技术部助理吧(看这就是千变万化的软件世界)

    复制代码
    public class CodeFarmerAssistant : ICodeFarmer
    {
        public void AddNewFunction()
        {
            //todo
        }
    }
    复制代码

    销售部门提需求需要跟这位新同事打交道。但是有时候助理他有些问题不明白还是要和技术经理提需求,这里

    private ICodeFarmer CodeFarmerManager;
    
    public SalesManager()
    {
        CodeFarmerManager = new XXXXXXXX();
    }

    就形成了一个变化的东西,但是代码里面你总要给new一个啥东西出来呀,怎么搞??

    好吧,现在只能这样:在构造函数中指明需要和啥玩意打交道

    复制代码
    public class SalesManager : ISalesman
    {
        private ICodeFarmer codeFarmer;
    
        public SalesManager(ICodeFarmer codeFarmer)
        {
            this.codeFarmer = codeFarmer;
        }
    
        public void AddNewFunction()
        {
            this.codeFarmer.AddNewFunction();
        }
    }
    
    //现在客户端代码要随着变化:
    
    public class ShaBClient : IClient
    {
        public void AddNewFunction()
        {
            ISalesman salesman = new SalesManager(new CodeFarmerAssistant());
            salesman.AddNewFunction();
        }
    }
    复制代码

    这样老问题又出来

    尼玛客户发怒了,又要老子和码农打交道
    ISalesman salesman = new SalesManager(new CodeFarmerAssistant());
    老子再也不想见到码农了,滚!!!

    你看这事办的,差点客户都丢了。那咋整啊!!

    这里请大家一定要注意一点,所谓的依赖解耦,不是说这玩意没依赖了,而是把依赖从一个地方转移到另一个地方,转移到什么地方呢?能转到什么地方,无非2个地方,因为这2地方好弄
    1. 配置文件中。随便就可以改改,不用弄程序
    2. 添加中间层,集中将依赖放到这里。这样好集中管理。

    我来分别说说这2点怎么弄
    弄到配置文件中:

    复制代码
    public class SalesManager : ISalesman
    {
        private ICodeFarmer codeFarmer;
    
        public SalesManager()
        {
            this.codeFarmer = Assembly.Load("Core").CreateInstance(ConfigurationManager.AppSettings["CodeFamerRepresent"]) as ICodeFarmer;
        }
    
        public void AddNewFunction()
        {
            this.codeFarmer.AddNewFunction();
        }
    }
    复制代码

    看,自从用了反射,妈妈再也用担心不能用配置文件创建对象了。老板再也不用担心客户发飙了。

    复制代码
    public class ShaBClient : IClient
        {
            public void AddNewFunction()
            {
                ISalesman salesman = new SalesManager();
                salesman.AddNewFunction();
            }
    }
    复制代码

    这样只要变更一下配置,就能控制到底谁是码农的代言人
    <appSettings>
        <add key="CodeFamerRepresent" value="CodeFarmerManager"/>
        <!--<add key="CodeFamerRepresent" value="CodeFarmerAssistant"/>-->
    </appSettings>
    看这不是挺好么?

    这里就引入了一个新的词,依赖注入

    如上面例子

    咱们不仅要知道注入方式(以免违反DI原则),也要知道注入的 时机(以免依赖不需要依赖的对象)

    啥玩意依赖注入,这么高深的词,实际上就是尼玛,咋初始化这个被依赖的对象IcodeFarmer,初始化了,依赖产生了,所以注入了依赖,这个词把他理解成 初始化被依赖的对象 更形象一点。关键就是在哪里初始化化这个对象。刚刚那个例子说了可以在构造函数中注入依赖,实际上还有一种属性注入,都不扯那么远了,既然可以通过构造函数注入,尼玛还需要了解属性注入干蛋,哥哥我学东西的原则就是不学多余的。只要明白最根本的道理就行。程序语言的技巧,咱没太多精力玩那个!

    那接下来说添加中间层来做这个事情是啥意思,简单呗,工厂模式就是干这个的

    我简单写一下就是:

    复制代码
    public class CodeFarmerFactory
    {
        public static ICodeFarmer GetCodeFarmer()
        {
            return new CodeFarmerAssistant();
            //return new CodeFarmerManager();
        }
    }
    
    public class SalesManager : ISalesman
    {
        private ICodeFarmer codeFarmer;
    
        public SalesManager()
        {
            this.codeFarmer = CodeFarmerFactory.GetCodeFarmer();
        }
    
        public void AddNewFunction()
        {
            this.codeFarmer.AddNewFunction();
        }
    }
    复制代码

    有的人说,你这不是扯淡吗?你具体的对象创建你还不是要改代码
    public static ICodeFarmer GetCodeFarmer()
    {
        return new CodeFarmerAssistant();
        //return new CodeFarmerManager();
    }
    我想说的是,你这不废话吗?你要变化东西,你不改就能变化?要么改代码,要么改配置文件,这里只是把变化集中到一起(工厂中)管理而已,不是说尼玛不用变!!!

    那是不是说就这样就完了,当然不是,如果是,我还写autofac干蛋。那还有什么需要考虑的事情呢,我现在先总结一下如下几点:

    1. 反射性能问题,你不能每次创建一个对象都反射一下,这样影响性能,有时我们需要把对象缓存起来,下次直接取缓存里面的对象,我们可以叫这个缓存为容器,这也是所以IOC容器里都有的一个概念
    2. 有了缓存,我们需要管理这个缓存对象的创建和销毁时间问题,不能老存在里面,或者说有些对象会过期,比如一些数据访问对象,当数据连接都释放了,你还保存这个对象,有蛋蛋用,一个访问web页面结束了,你还保存Controller有蛋蛋用?所以有个生命周期的概念。
    3. 当缓存对象销毁的时候,引用的非托管资源怎么办!

    其他的问题我之后说到autofac的时候再来具体讲,现在一下列出来太突兀,没什么用。

    希望通过这一篇文章说明了一些基本的概念,如果有什么有问题的地方希望大家提出讨论

     
     
     
    标签: autofac依赖注入DIioc
  • 相关阅读:
    IntelliJ Idea和IntelliJ webstrm 常用快捷键
    mybatis基础学习2---(resultType和resultMap的用法和区别)和setting的用法
    使用观察者模式观察线程的生命周期
    观察者设计模式介绍
    java的内存模型及缓存问题
    一个解释volatile关键字作用的最好的例子
    多线程的waitset详细介绍
    浅谈单例模式
    SimpleThreadPool给线程池增加自动扩充线程数量,以及闲时自动回收的功能
    SimpleThreadPool给线程池增加拒绝策略和停止方法
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3465298.html
Copyright © 2020-2023  润新知