了解IOC

IOC介绍

术语:控制反转(IoC)、依赖倒置原则(DIP)、依赖注入(DI)、IoC容器。

IoC和DIP是在设计应用程序类时应该使用的高级设计原则。由于它们是原则,它们推荐了某些最佳实践,但没有提供任何具体的实现细节。依赖注入(DI)是一种模式,而IoC容器是一个框架。

使用控制反转(IoC)、依赖倒置原则(DIP)、依赖注入(DI)就是为了实现松耦合设计,是代码更容易扩展和维护。
在这里插入图片描述

IoC是一种设计原则,它建议在面向对象设计中反转不同类型的控件,以实现应用程序类之间的松耦合。在这种情况下,控制指的是类除了其主要职责之外的任何其他职责,比如控制应用程序的流,或者控制依赖对象的创建和绑定。

DIP原则还有助于实现类之间的松耦合。强烈建议同时使用DIP和IoC,以实现松耦合。
DIP建议高级模块不应该依赖于低级模块。两者都应该依赖于抽象。

依赖注入(Dependency Injection, DI)是一种设计模式,它实现了IoC原则来反转依赖对象的创建。

IoC容器是一个用于在整个应用程序中管理自动依赖注入的框架,.net有各种各样的IoC容器,比如Unity, Ninject, StructureMap, Autofac等等。

使用IOC将高耦合转向低耦合过程:
在这里插入图片描述

工厂类控制反转

IoC可以在创建依赖类的对象时应用。
类A调用b.SomeMethod()来完成其任务k1。没有类B,类A无法完成它的任务,可以说“类A依赖于类B”或“类B依赖于类A”。

public class A
{
    B b;
    public A()
    {
        b = new B();
    }
    public void Task1() 
    {
        b.SomeMethod();
    }
}

public class B
{
    public void SomeMethod()
    { 
    }
}

IoC原则建议反转控制。这意味着将创建对象实例委托给另一个类。换句话说,将依赖项创建对象实例从类A转换到另一个类。类A使用Factory类来获取类b的对象。因此,我们将依赖对象的创建从类A倒转到工厂。类A不再创建类B的对象,而是使用工厂类来获取类B的对象。

public class A
{
    B b;
    public A()
    {
        b = Factory.GetObjectOfB ();
    }
    public void Task1() {
        b.SomeMethod();
    }
}
public class B
{
    public void SomeMethod()
    { 
    }
}
public class Factory
{
    public static B GetObjectOfB() 
    {
        return new B();
    }
}

在面向对象的设计中,类应该以松散耦合的方式设计。松耦合意味着一个类中的更改不应该强制其他类更改,这样整个应用程序就可以变得可维护和可扩展。用典型的n层架构来理解这一点,如下图所示:
在这里插入图片描述
在典型的n层体系结构中,用户界面(UI)使用服务层(Service)检索或保存数据。服务层(Service)使用BusinessLogic类。BusinessLogic类依赖于DataAccess类。这是简单的n层架构设计。下面从BusinessLogic类和DataAccess类来理解IoC。

public class CustomerBusinessLogic
{
    DataAccess _dataAccess;

    public CustomerBusinessLogic()
    {
        _dataAccess = new DataAccess();
    }

    public string GetCustomerName(int id)
    {
        return _dataAccess.GetCustomerName(id);
    }
}

public class DataAccess
{
    public DataAccess()
    {
    }

    public string GetCustomerName(int id) {
        return "Dummy Customer Name"; // get it from DB in real app
    }
}

CustomerBusinessLogic类依赖于DataAccess类。它创建DataAccess类的一个对象来获取客户数据。
CustomerBusinessLogic和DataAccess是紧密耦合的类,因为CustomerBusinessLogic类包含具体DataAccess类的引用。它还创建DataAccess类的对象并管理该对象的生存期。存在如下问题:
1.DataAccess类中的更改将导致CustomerBusinessLogic类的更改。例如,如果我们在DataAccess类中添加、删除或重命名任何方法,则需要相应地更改CustomerBusinessLogic类。
2.假设客户数据来自不同的数据库或web服务,并且在将来,我们可能需要创建不同的类,因此这将导致CustomerBusinessLogic类的更改。
3.因为CustomerBusinessLogic类创建了具体DataAccess类的一个对象,所以不能对它进行独立测试(TDD)。不能用模拟类替换DataAccess类。

为了解决上述所有问题,实现松耦合设计,可以将IoC原则和DIP原则结合使用。记住,IoC是原则,不是模式。它只给出了高级设计指导原则,但没有给出实现细节。您可以按照自己想要的方式自由地实现IoC原则。
在这里插入图片描述
使用工厂模式来实现IoC,作为实现松散耦合类的第一步

public class DataAccessFactory
{
    public static DataAccess GetDataAccessObj() 
    {
        return new DataAccess();
    }
}
public class CustomerBusinessLogic
{

    public CustomerBusinessLogic()
    {
    }

    public string GetCustomerName(int id)
    {
        DataAccess _dataAccess =  DataAccessFactory.GetDataAccessObj();

        return _dataAccess.GetCustomerName(id);
    }
}

依赖倒置原则

依赖倒置的定义:
高级模块不应该依赖于低级模块。两者都应该依赖于抽象。
抽象不应该依赖于细节。细节应该依赖于抽象。

根据DIP定义,高级模块不应该依赖于低级模块。两者都应该依赖于抽象。因此,首先确定哪个是高级模块(类),哪个是低级模块。在上面使用工厂类解决CustomerBusinessLogic类依赖于DataAccess类的示例中,CustomerBusinessLogic依赖于DataAccess类,因此CustomerBusinessLogic是高级模块,而DataAccess是低级模块。但是,根据DIP的第一条规则,CustomerBusinessLogic不应该依赖于具体的DataAccess类,相反,两个类都应该依赖于抽象。DIP中的第二个规则是“抽象不应该依赖于细节。”细节应该依赖于抽象”。上面的实例虽然使用了工厂类来创建对象,但是CustomerBusinessLogic类中还是有DataAccess类型引用,还是存在依赖关系;下面将遵循依赖倒置进行优化。

先说下什么是抽象:抽象和封装是面向对象编程的重要原则。不同的人有许多不同的定义,但是让我们使用上面的示例来理解抽象。
在英语中,抽象是指不具体的东西。在编程术语中,上面的CustomerBusinessLogic和DataAccess是具体类,这意味着我们可以创建它们的对象。因此,编程中的抽象意味着创建一个非具体的接口或抽象类。这意味着我们不能创建接口或抽象类的对象。根据DIP, CustomerBusinessLogic(高级模块)不应该依赖于具体的DataAccess类(低级模块)。两个类都应该依赖于抽象,这意味着两个类都应该依赖于一个接口或一个抽象类。

实例代码进一步修改

public interface ICustomerDataAccess
{
    string GetCustomerName(int id);
}

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess()
    {
    }

    public string GetCustomerName(int id) {
        return "Dummy Customer Name";        
    }
}

public class DataAccessFactory
{
    public static ICustomerDataAccess GetCustomerDataAccessObj() 
    {
        return new CustomerDataAccess();
    }
}

public class CustomerBusinessLogic
{
    ICustomerDataAccess _custDataAccess;

    public CustomerBusinessLogic()
    {
        _custDataAccess = DataAccessFactory.GetCustomerDataAccessObj();
    }

    public string GetCustomerName(int id)
    {
        return _custDataAccess.GetCustomerName(id);
    }
}

示例中实现了DIP,其中高级模块(CustomerBusinessLogic)和低级模块(CustomerDataAccess)依赖于抽象(ICustomerDataAccess)。而且,抽象(ICustomerDataAccess)并不依赖于细节(CustomerDataAccess),但是细节依赖于抽象。

实现DIP的优点是,CustomerBusinessLogic和CustomerDataAccess类是松散耦合的类,因为CustomerBusinessLogic不依赖于具体的DataAccess类,而是包含对ICustomerDataAccess接口的引用。现在,我们可以轻松地使用另一个用不同实现实现ICustomerDataAccess的类。

依赖注入

上面的实例中还是没有实现完全松散耦合的类,因为CustomerBusinessLogic类包含一个工厂类来获取ICustomerDataAccess的引用。这就是依赖注入模式帮助我们的地方。下面将通过上面的例子学习如何使用依赖注入(DI)和策略模式。

依赖注入(DI)是一种用于实现IoC的设计模式。它允许在类之外创建依赖对象,并通过不同的方式将这些对象提供给类。使用DI,我们可以将依赖对象的创建和绑定移到依赖它们的类之外。

依赖注入模式涉及3种类型的类。
1.Client Class:客户端类(依赖类)是一个依赖于服务类的类。
2.Service Class:服务类(依赖项)是向客户端类提供服务的类。
3.Injector Class:注入器类将服务类对象注入到客户端类中。
在这里插入图片描述
注入器类创建了服务类的一个对象,并将该对象注入到客户机对象中。通过这种方式,DI模式将创建服务类对象的责任从客户端类中分离出来。

注入器类将服务(依赖项)注入到客户端(依赖项)。注入器类通过三种方式广泛注入依赖项:通过构造函数、属性或方法。

构造函数注入:在构造函数注入中,注入器通过客户端类构造函数提供服务(依赖项)。
属性注入:在属性注入(又名Setter注入)中,注入器通过客户端类的公共属性提供依赖关系。
方法注入:在这种类型的注入中,客户端类实现一个接口,该接口声明提供依赖项的方法,注入器使用这个接口向客户端类提供依赖项。

在上面的示例中使用了工厂类和依赖倒置原则。但是在CustomerBusinessLogic类中使用了DataAccessFactory。因此,假设存在ICustomerDataAccess的另一个实现,希望在CustomerBusinessLogic中使用这个新类。还需要更改CustomerBusinessLogic类的源代码。依赖注入模式通过构造函数、属性或接口注入依赖对象来解决这个问题。在封装多一层CustomerService类成为注入器类,它通过构造函数、属性或方法将CustomerDataAccess对象设置到CustomerBusinessLogic,以实现松耦合。
在这里插入图片描述
构造函数注入

public class CustomerBusinessLogic
{
    ICustomerDataAccess _dataAccess;
    //构造函数注入
    public CustomerBusinessLogic(ICustomerDataAccess custDataAccess)
    {
        _dataAccess = custDataAccess;
    }

    public string ProcessCustomerData(int id)
    {
        return _dataAccess.GetCustomerName(id);
    }
}

public interface ICustomerDataAccess
{
    string GetCustomerName(int id);
}

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess()
    {
    }

    public string GetCustomerName(int id) 
    {     
        return "Dummy Customer Name"; 
    }
}

//把CustomerBusinessLogic和CustomerDataAccess依赖关系封装到CustomerService
//customerbusiness逻辑和CustomerDataAccess类就变成了“更”松散耦合的类
public class CustomerService
{
    CustomerBusinessLogic _customerBL;

    public CustomerService()
    {
        _customerBL = new CustomerBusinessLogic(new CustomerDataAccess());
    }

    public string GetCustomerName(int id) {
        return _customerBL.ProcessCustomerData(id);
    }
}

属性注入

public interface ICustomerDataAccess
{
    string GetCustomerName(int id);
}

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess()
    {
    }

    public string GetCustomerName(int id) 
    {     
        return "Dummy Customer Name"; 
    }
}
public class CustomerBusinessLogic
{
    public CustomerBusinessLogic()
    {
    }
    
    public string GetCustomerName(int id)
    {
        return DataAccess.GetCustomerName(id);
    }
	//DataAccess做为CustomerBusinessLogic属性,实现属性注入
    public ICustomerDataAccess DataAccess { get; set; }
}

public class CustomerService
{
    CustomerBusinessLogic _customerBL;

    public CustomerService()
    {
        _customerBL = new CustomerBusinessLogic();
        //在CustomerService中对CustomerBusinessLogic.DataAccess进行实例化,属性注入
        _customerBL.DataAccess = new CustomerDataAccess();
    }

    public string GetCustomerName(int id) {
        return _customerBL.GetCustomerName(id);
    }
}

CustomerBusinessLogic类包括名为DataAccess的公共属性,可以在其中设置实现ICustomerDataAccess的类的实例。因此CustomerService类使用这个公共属性创建和设置CustomerDataAccess类。

方法注入

public interface ICustomerDataAccess
{
    string GetCustomerName(int id);
}

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess()
    {
    }

    public string GetCustomerName(int id) 
    {     
        return "Dummy Customer Name"; 
    }
}
interface IDataAccessDependency
{
    void SetDependency(ICustomerDataAccess customerDataAccess);
}

public class CustomerBusinessLogic : IDataAccessDependency
{
    ICustomerDataAccess _dataAccess;

    public CustomerBusinessLogic()
    {
    }

    public string GetCustomerName(int id)
    {
        return _dataAccess.GetCustomerName(id);
    }
        
    public void SetDependency(ICustomerDataAccess customerDataAccess)
    {
        _dataAccess = customerDataAccess;
    }
}

public class CustomerService
{
    CustomerBusinessLogic _customerBL;

    public CustomerService()
    {
        _customerBL = new CustomerBusinessLogic();
        ((IDataAccessDependency)_customerBL).SetDependency(new CustomerDataAccess());
    }

    public string GetCustomerName(int id) {
        return _customerBL.GetCustomerName(id);
    }
}

CustomerBusinessLogic类实现了IDataAccessDependency接口,其中包括SetDependency() 。因此注入器类CustomerService现在将使用此方法将依赖类(CustomerDataAccess)注入到CustomerBusinessLogic 类。

IoC容器

我们已经使用了一些原则和模式来实现松散耦合的类。在专业项目中,有许多依赖类,都实现这些模式非常耗时。下面讲介绍解IoC容器,IoC容器(即DI容器)是一个用于实现自动依赖注入的框架。它管理对象的创建和生命周期,还向类注入依赖关系。

IoC容器创建指定类的对象,并在运行时通过构造函数、属性或方法注入所有依赖项对象,并在适当的时候对其进行配置。这样做,我们就不必手动创建和管理对象。

所有容器必须为以下DI生命周期提供简单的支持。
1.Register:当容器遇到特定类型时,它必须知道实例化哪个依赖项。这个过程称为注册。基本上,它必须包含一些注册类型映射的方法。
2.Resolve:在使用IoC容器时,我们不需要手动创建对象。容器为我们做到了这一点。容器必须包含解析指定类型的一些方法;容器创建指定类型的对象,注入所需的依赖项(如果有的话)并返回该对象。
3.Dispose:容器必须管理依赖对象的生存期。大多数IoC容器包括不同的生命周期管理来管理和释放对象的生命周期。

有许多开源或商业的.net容器可用。下面列出了一些。
Unity
StructureMap
Castle Windsor
Ninject
Autofac
DryIoc
Simple Injector
Light Inject

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值