ioc控制反转_AOP与IoC专题课(二)依赖倒置与控制反转

本文介绍了依赖倒置原则(DIP)及其在传统三层架构中的问题,强调了高层模块应依赖抽象而非具体实现。接着探讨了控制反转(IoC)和依赖注入(DI)的概念,以及它们如何降低模块间的耦合。此外,文章还详细讲解了动态代理在AOP中的应用,并以Unity框架为例展示了如何实现IoC和AOP。最后,通过代码示例展示了Unity容器的使用方法和依赖注入的实现。

02 依赖倒置与控制反转

课程回顾

什么是AOP

AOP与OOP的关系和区别

静态代理(装饰器模式、代理模式)实现AOP

装饰器模式与代理模式的区别

课程目标

动态代理实现AOP

依赖倒置原则

控制反转

Unity基本使用

动态代理实现AOP

动态代理实现AOP的方式有为两种,一种是通过代码织入的方式。

PostSharp 可以实现AOP静态织入。

静态织入就是直接修改IL中间语言,它是对编译器的一种扩展,在编译的时候,直接把我们要添加的功能,编译进IL中。

另一种是通过反射来完成,这个我们不需要自己去做,已经有一些实现AOP的框架,比如Romoting(分布式通信框架)、Castle(从数据访问框架ORM到IOC容器,再到WEB层的MVC框架、AOP,基本包括了整个开发过程中的所有东西)、Unity

Romoting
  1. 服务类与上例相同,创建动态代理类

    public class DynamicProxy<T> : RealProxy{    private readonly T _target;    // 执行之前    public Action BeforeAction { get; set; }    // 执行之后    public Action AfterAction { get; set; }    // 被代理泛型类    public DynamicProxy(T target) : base(typeof(T))    {        _target = target;    }    // 代理类调用方法    public override IMessage Invoke(IMessage msg)    {        var reqMsg = msg as IMethodCallMessage;        var target = _target as MarshalByRefObject;        BeforeAction();        var result = RemotingServices.ExecuteMessage(target, reqMsg);        AfterAction();        return result;    }}
  2. 创建代理工厂类

    public class ProxyFactory{    public static T Create<T>(Action before, Action after)    {        // 实例化被代理泛型对象        T instance = Activator.CreateInstance<T>();        // 实例化动态代理        var proxy = new DynamicProxy<T>(instance) {BeforeAction = before, AfterAction = after};        // 返回透明代理对象        return (T)proxy.GetTransparentProxy();    }}
  3. 调用执行

    static void Main(string[] args){    var acount = ProxyFactory.Create<AccountService>(() =>    {        Console.WriteLine("注册之前");    }, () =>    {        Console.WriteLine("注册之后");    });    public class User    {        public string Name { get; set; }        public string Password { get; set; }    }    acount.Reg(user);}
Unity
  1. 使用 NuGet 安装 Unity 和 Unity.Interception 包

    Install-Package UnityInstall-Package Unity.Interception
  2. 创建拦截器类

    public class AccountInterceptor : IInterceptionBehavior{    ///     /// Invoke方法接受2个参数:    /// input包含了调用的客户对象的方法名称和参数值的信息    /// getNext是一个委托,定义了拦截管道(或者代理对象)中要调用的下一个拦截行为。    /// 最终,它将调用结果返回给管道中的前一个拦截行为。    ///     ///     ///     ///     public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)    {        User user = input.Inputs[0] as User;        if (user.Password.Length < 10)        {            return input.CreateExceptionMethodReturn(new Exception("密码长度不能小于10位"));        }        Console.WriteLine("参数检测无误");        var result = getNext()(input, getNext);        Console.WriteLine("之后");        return result;    }    ///     /// GetRequiredInterface方法允许我们指定关联到该拦截行为的接口类型。    /// 在示例中,我们将在拦截器注册时指定接口类型,因此这个方法返回Type.EmptyTypes。    ///     ///     public IEnumerable<Type> GetRequiredInterfaces()    {        return Type.EmptyTypes;    }    ///     /// WillExecute属性允许我们通过定义该行为是否应该执行来优化行为链;    /// 这个属性永远返回true,因此这个拦截行为将始终执行。    ///     public bool WillExecute => true;}
  3. Main方法中注册服务和拦截器

    IUnityContainer container = new UnityContainer();container.AddNewExtension<Interception>();// RegisterType方法的第一个参数——Interceptor对象,定义了拦截器的类型,// 另外一个参数则注册了AccountInterceptor拦截器。container.RegisterType<IAccountService, AccountService>(    new Interceptor<InterfaceInterceptor>(),    new InterceptionBehavior<AccountInterceptor>());
  4. 然后获取实例

    // account变量并非是AccountService类型的,而是一个动态创建、实现了IAccountService接口的代理对象// 这个代理对象包含了IAccountService接口定义的方法、属性和事件var account = container.Resolve<IAccountService>();account.Reg(user);
Castel

https://www.cnblogs.com/youring2/p/10962573.html

作业1

什么是AOP?要求:非开发人员也能听明白,可以举例说明。

传统三层架构

通常意义上的三层架构就是将整个应用划分为:

  • 界面层(User Interface layer)

    主要表示WEB方式,也可以表示成WINFORM方式,如果逻辑层相当强大和完善,无论表现层如何定义和更改,逻辑层都能完善地提供服务。

  • 业务逻辑层(Business Logic Layer)

    主要是针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理,如果说数据层是积木,那逻辑层就是对这些积木的搭建。

  • 数据访问层(Data access layer)

    主要是对非原始数据(数据库或者文本文件等存放数据的形式)的操作层,而不是指原始数据,也就是说,是对数据库的操作,而不是数据,具体为业务逻辑层或表示层提供数据服务.

c7149e8842861dfbb71c7eab752eccae.png

区分层次的目的是为了实现“高内聚低耦合”的思想。

三层架构的缺点

界面层依赖业务逻辑层,业务逻辑层依赖数据访问层,这种自上而下的层层依赖会导致级联的修改,如果底层发生了修改,则可能需要逐层进行修改。

传统的三层架构,由于自上而下的依赖,很难顺利的实现团队协同开发,比如负责业务层的人,在数据层没有编写完是无法开始工作的。

因为,上层的功能取决于底层功能的实现,这不利于团队开发,只有高内聚,没有低耦合。

依赖倒置原则

依赖倒置原则,英文缩写DIP,全称Dependence Inversion Principle。

定义:

  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;

  • 抽象不应该依赖于实现,实现应该依赖于抽象。

上面的定义不难理解,主要包含两次意思:

  • 高层模块不应该直接依赖于底层模块的具体实现,而应该依赖于底层的抽象。换言之,模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。

  • 接口和抽象类不应该依赖于实现类,而实现类依赖接口或抽象类。“面向接口编程”思想正是这点的最好体现。

依赖倒置与三层

传统三层架构中,UI层依赖于BLL层,BLL层依赖于DAL层。

由于每一层都是依赖于下层的实现,这样当某一层的结构发生变化时,它的上层就不得不也要发生改变,比如我们DAL里面逻辑发生了变化,可能会导致BLL和UI层都随之发生变化,这种架构是非常荒谬的!

如果我们换一种设计思路,高层模块不直接依赖低层的实现,而是依赖于低层模块的抽象。

52e9f381cc160d625fb88e4d35cc6ca2.png

具体表现为我们增加一个IBLL层,里面定义业务逻辑的接口,UI层依赖于IBLL层,BLL层实现IBLL里面的接口,所有具体的业务逻辑则定义在BLL里面,这个时候如果我们BLL里面的逻辑发生变化,只要接口的行为不变,上层UI里面就不用去改变。

依赖倒置的目的

在传统三层里面,高层模块直接依赖低层模块的实现,当我们将高层模块依赖于底层模块的抽象时,就好像依赖“倒置”了。这就是依赖倒置的由来。

通过依赖倒置,可以使得架构更加稳定、更加灵活、更好应对需求变化。

依赖倒置原则就是基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。

以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。

在三层架构里面增加一个接口层能实现依赖倒置,它的目的就是降低层与层之间的耦合,使得设计更加灵活。

在介绍依赖倒置具体如何使用之前,我们需要引入IoC相关的概念,我们先来看看它们之间的关系。

控制反转

IoC 即控制反转 (Inversion of Control),一种反转流、依赖和接口的方式(DIP的具体实现方式),它把传统上由程序代码直接操控的对象的调用权交给第三方,通过第三方来实现对象组件的装配和管理。

假设你是一个五岁的小孩,想要吃点东西,如果你自己从冰箱拿食物,你可能会让门开着,还可能会得到父母不想让你拥有的东西,甚至可能在寻找一些我们甚至没有或者已经过期的东西。

你应该做的是陈述一下需求,比如"我饿了,想要吃XX",你的父母就会做好你需要的食物,然后拿给你。

从你想吃东西的需求,主动去冰箱拿(正转)食物,到你想要吃的食物而被动获得(反转),前者你主动从冰箱获得,而后者由你的父母,也就是第三方把食物给你,而你并不关心食物从哪来,以及怎么得来。

所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。

通俗地说,就是应用程序本身不负责依赖对象的创建和维护,而是将它交给第三方来负责,这样控制权就由应用程序转移到了第三方,即控制权实现了所谓的反转。

例如在类型A中需要使用类型B的实例,而B 实例的创建并不由A 来负责,而是通过第三方来创建。

依赖注入(DI)

依赖注入即DI(Dependency Injection),是IoC的一种实现方式,用来反转依赖。

场景

假设你是一个五岁的小孩,想要吃点东西,如果你自己从冰箱拿食物,你可能会让门开着,还可能会得到父母不想让你拥有的东西,甚至可能在寻找一些我们甚至没有或者已经过期的东西。

你应该做的是陈述一下需求,比如"我饿了,想要吃XX",你的父母就会做好你需要的食物(依赖),然后拿给你(注入)。

依赖注入不是目的,它是一系列工具和手段,最终的目的是帮助我们开发出松散耦合、可维护、可测试的代码和程序。

在调用一个类的时候,先要实例化这个类,生成一个对象。如果你在写一个类,过程中要调用到很多其它类,甚至这里的其它类,也要“依赖”于更多其它的类,那么可以想象,你要进行多少次实例化,依赖关系有多么复杂。

如果面试官问你,为什么要依赖注入,你可以一句话回答他,因为我们要实现控制反转。

如果面试官继续问,为什么要控制反转,因为我们的软件设计需要符合依赖倒置原则。

IoC容器

IoC 即控制反转 (Inversion of Control),一种反转流、依赖和接口的方式(DIP的具体实现方式),它把传统上由程序代码直接操控的对象的调用权交给第三方,通过第三方来实现对象组件的装配和管理。

这里的第三方,有一个专用名词叫IoC容器。

IoC容器是由依赖注入框架提供的,它是依赖注入框架的主心骨,它主要用来映射依赖,管理对象的创建和生存周期。

假设你是一个五岁的小孩,想要吃点东西,如果你自己从冰箱拿食物,你可能会让门开着,还可能会得到父母不想让你拥有的东西,甚至可能在寻找一些我们甚至没有或者已经过期的东西。

你应该做的是陈述一下需求,比如"我饿了,想要吃XX",你的父母(IoC容器)就会做好你需要的食物(依赖),然后拿给你(注入)。

IoC容器本身也是个对象,你把某个类(不管有多少依赖关系)放入这个容器中,它都可以“解析”出这个类的实例,并且还可以把这个类依赖的对象,也实例化出来,然后注入进去。

所谓的依赖注入就是把有依赖关系的类放入IoC容器中,然后解析出这个类的实例。

实现IoC容器的框架有很多,.Net中常用的有:Unity、Autofac、Ninject。

案例

场景是这样的,父亲给孩子讲故事,只要给她一本书,他就可以照着书给孩子讲故事了。

传统方式

不经过任何设计模式和设计原则加工的传统OOP方式。

public class Book{    public string GetContent()    {           return "从前有座山,山上有座庙.....";      }}public class Father{    public void Read()    {        Book book = new Book();        Console.WriteLine("爸爸开始给孩子讲故事了");        Console.WriteLine(book.GetContent());    }}public class Program{    public static void Main()    {        Father father = new Father();        father.Read();    }}

从关系图上可以很明显的看出来,Father类直接依赖Book

e52ed7373026dfe978e65222cf21c204.png

但是如果有一天需求改变了:不是给书,而是给了一份报纸,让这位父亲读一下报纸上的新闻。

public class Paper
{
public string GetContent()
{
return "王聪聪被限制高消费......";
}
}

如果还是使用之前的Father类会发现,爸爸这时不会读报纸上的新闻。只是将书换成了报纸,我们就要去修改Father类的代码才能实现。

假如以后报纸换成杂志呢?换成网页呢?还要不断地修改Father类,这显然不是好的设计。原因就是Father与Book之间的耦合性太高了,必须降低他们之间的耦合度才行。

工厂模式

工厂模式是我们最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

所以这里可以引入一个抽象的接口,IReader——读物,只要是可以阅读的都属于读物。

public interface IReader
{
string GetContent();
}

创建一个读物工厂类,以后有其它读物,就可以在工厂方法中添加:

public static class ReaderFactory
{
public static IReader GetReader(string readerType)
{
if(string.IsNullOrEmpty(readerType)){
return null;
}
switch (readerType)
{
case "Paper":
return new Paper();
case "Book":
return new Book();
default:
return null;
}
}
}

修改Father类:

public class Father
{
public IReader Reader { get; set; }
public Father(string readerName)
{
Reader = ReaderFactory.GetReader(readerName);
}
public void Read()
{
Console.WriteLine("爸爸开始给孩子讲故事了");
Console.WriteLine(Reader.GetContent());
}
}

Father类与接口IReader发生依赖关系,而Book和Paper都属于读物,它们各自都去实现IReader接口,这样就符合依赖倒置原则了。

ef7e41f684974c98ae806a153018b3e9.png

从关系图中,可以看出来,Father类不再依赖Book类了,而是依赖工厂类和接口,这里就符合了依赖导致原则:

  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;

  • 抽象不应该依赖于实现,实现应该依赖于抽象。

我们可以把 IoC容器 看做是工厂模式的升华,把 IoC容器 看做是一个带有配送物流的超级工厂。

依赖注入

为什么说IoC容器是带有配送物流的超级工厂呢,因为IoC容器不仅能够创建对象,还可以把对象送到需求方的手里,也就是注入进使用方中。

依赖注入常见的方式有两种:构造函数注入和属性注入。

修改 Father 类

public class Father
{
public IReader Reader { get; set; }
public Father(IReader reader)
{
Reader = reader;
}
public void Read()
{
Console.WriteLine("爸爸开始给孩子讲故事了");
Console.WriteLine(Reader.GetContent());
}
}

从关系图中,可以发现,Father类只依赖接口,连工厂类也不再依赖。

c0600ab8a5740557406a6289c275aa83.png

Unity基本使用

Unity容器是一款功能齐全的通用IoC容器,可用于任何类型的.NET应用程序。它是开源的,并在Apache 2.0许可下发布。

Unity易于扩展,可扩展性强。任何人都可以编写可更改容器行为或添加新功能的扩展。例如,Unity提供的AOP拦截功能就属于Unity的容器扩展。

static void Main(string[] args)
{
// 创建容器对象
var container = new UnityContainer();
// 在容器里注册 接口与实现类,创建依赖关系
container.RegisterType();
// 在容器里注册Father类
container.RegisterType();
// 从容器里拿出要使用的类,容器会自行创建Father对象
// 并且会从构造函数注入Father所依赖的对象
var father = container.Resolve();
father.Read();
}

dfc7ec78e9049ba95c17eaf435ae9a6d.gif

关注"软谋dotnet"公众号

编号 文件名称 cwts-specs-001 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:名语术语 cwts-specs-002 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:概述 cwts-specs-003 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:物理信道和传输信道到物理信道的映射 cwts-specs-004 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:信道编码与复用 cwts-specs-005 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:扩频与调制 cwts-specs-006 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:物理层过程 cwts-specs-007 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:物理层测量 cwts-specs-008 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:物理层向上层提供的服务 cwts-specs-009 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:MAC协议 cwts-specs-010 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:RLC协议 cwts-specs-011 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:PDCP协议 cwts-specs-012 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:BMC协议 cwts-specs-013 IMT-DS FDD(WCDMA)系统无线接口层3技术规范:RRC协议 cwts-specs-014 IMT-DS FDD(WCDMA)系统Iu接口技术规范:概述 cwts-specs-015 IMT-DS FDD(WCDMA)系统Iu接口技术规范:层1技术要求 cwts-specs-016 IMT-DS FDD(WCDMA)系统Iu接口技术规范:信令传输 cwts-specs-017 IMT-DS FDD(WCDMA)系统Iu接口技术规范:RANAP信令 cwts-specs-018 IMT-DS FDD(WCDMA)系统Iu接口技术规范:数据传输和传输信令 cwts-specs-019 IMT-DS FDD(WCDMA)系统Iu接口技术规范:用户平面协议 cwts-specs-020 IMT-DS FDD(WCDMA)系统Iub接口技术规范:概述 cwts-specs-021 IMT-DS FDD(WCDMA)系统Iub接口技术规范:层1技术要求 cwts-specs-022 IMT-DS FDD(WCDMA)系统Iub接口技术规范:信令传输 cwts-specs-023 IMT-DS FDD(WCDMA)系统Iub接口技术规范:NBAP信令 cwts-specs-024 IMT-DS FDD(WCDMA)系统Iub接口技术规范:用于CCH数据流的数据传输和传输信令 cwts-specs-025 IMT-DS FDD(WCDMA)系统Iub接口技术规范:用于CCH数据流的用户平面协议 cwts-specs-026 IMT-DS FDD(WCDMA)系统Iur接口技术规范:概述 cwts-specs-027 IMT-DS FDD(WCDMA)系统Iur接口技术规范:层1技术要求 cwts-specs-028 IMT-DS FDD(WCDMA)系统Iur接口技术规范:信令传输 cwts-specs-029 IMT-DS FDD(WCDMA)系统Iur接口技术规范:RNSAP信令 cwts-specs-030 IMT-DS FDD(WCDMA)系统Iur接口技术规范:用于CCH数据流的数据传输和传输信令 cwts-specs-031 IMT-DS FDD(WCDMA)系统Iur接口技术规范:用于CCH数据流的用户平面协议 cwts-specs-032 IMT-DS FDD(WCDMA)系统Iub/Iur接口技术规范:用于DCH数据流的数据传输和传输信令 cwts-specs-033 IMT-DS FDD(WCDMA)系统Iub/Iur接口技术规范:用于DCH数据流的用户平面协议 cwts-specs-034 TD-SCDMA系统无线接口物理层技术规范 cwts-specs-035 TD-SCDMA系统无线接口层2技术规范 cwts-specs-036 TD-SCDMA系统无线接口层3-RRC技术规范 cwts-specs-037 TD-SCDMA系统Iu接口技术规范 cwts-specs-038 TD-SCDMA系统Iub接口技术规范 cwts-specs-039 TD-SCDMA系统Iur接口技术规范 cwts-specs-040 TD-SCDMA系统基站设备无线收发特性技术规范 cwts-specs-041 TD-SCDMA系统用户终端设备无线收发特性技术规范 CWTS发布的研究报告列表 cwts-reports-001 IMT-DS FDD(WCDMA)系统连接模式下的层间过程(25.303)标准研究报告 cwts-reports-002 IMT-DS FDD(WCDMA)系统空闲模式下UE的流程和连接模式下小区重选流程(25.304)标准研究报告 cwts-reports-003 IMT-DS FDD(WCDMA)系统无线资源管理RRM研究报告 cwts-reports-004 IMT-DS FDD(WCDMA)系统无线资源管理RRM研究报告 cwts-reports-005 IMT-DS FDD(WCDMA)系统UE无线接入能力研究报告
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值