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
原文地址:https://www.cnblogs.com/Leo_wl/p/3465298.html