控制反转 IOC

2020-09-25  本文已影响0人  wyc0859

一步步道来

一:OCP 开闭原则

1、interface统一方法调用
2、只有一段代码中没有new的出现,才能保持代码的相对稳定,才能逐步实现OCP
3、但代码中总是会存在不稳定,所以解决办法是:隔离这些不稳定,保证其他的代码是稳定的
4、工厂模式+反射:统一对象的实例化。但这并不是IOC
5、工厂模式+反射,实例化后放缓存提供性能

工厂模式+反射

public class Main {
    public static void main(String[] args) throws Exception {
        String name=Main.getInput();
        IBase iBase= Factory.getUser(name);
        iBase.print();
    }
    private static String getInput(){
        System.out.println("请输入");
        Scanner scanner=new Scanner(System.in);
        return scanner.nextLine();
    }
} 

package com.company.test;
public class Factory {
    public static IBase getUser(String name) throws Exception {
        //变化导致不稳定,用户输入-选择就是变化,所以这里是隔离了变化 
        String classStr="com.company.test."+name;
        //反射
        Class <?> cla=Class.forName(classStr);
        Object obj=cla.newInstance();
        return (IBase)obj;
    }
}

.

二、依赖注入 DI

依赖注入是IOC其中的一种表现

一个A类要依赖于注入的B类才能实现作用,就是A依赖于B

public Class A{ 
  public void fun(){
     B b=new B();
     b.print();
  }

如果这个B类是注入的,那就叫依赖注入
注入的类一定是实现接口的,不然就违法了OPC,实例的名称也可以直接是接口名称(需同接口下只有一个在容器里)

public Class A{
  private ICa ca;  //属性注入;仅演示,并不是这样就直接注入成功
  public void fun(){
      ca.print();
  }

常见的依赖注入方式:

  • 属性注入
  • 构造注入

.

三、容器

容器的作用是装配对象;将各种注入,类的衔接,装配在一起,又起到隔离风险的效果

public void user(){
        Iface ic=new C();
        A a=new A(ic);    //构造注入
        A a1=new A();
        a1.setIc(ic);    //属性注入
 }

.

四、控制反转 IOC

上面一堆的铺垫介绍,合并起来就是控制反转

原来是在A类中实例化C类并调用方法,那A控制C,A是控制方

public Class A{
  private C c=new C();
  public void fun(){
      c.print();
  }

由于使用了依赖注入,C不再由A控制,不引入C,也不实例化C。引入的是接口 ca自动匹配名称(spring的约定)

public Class A{
  private ICa ca;   
  public void fun(){
      ca.print();
  }

通过外部传入依赖,而不是自己创建依赖。那么问题来了,谁把依赖传给他们,答案是IoC容器。
类A依赖类C的强耦合关系可以在运行时通过容器建立,也就是说把创建C实例的工作移交给容器,类A只管使用就可以。控制方由A变成了容器,实现了控制反转
.

为了理解这几个概念,我们还是用上面汽车的例子。只不过这次换成代码。我们先定义四个Class,车,车身,底盘,轮胎。然后初始化这辆车,最后跑这辆车。代码结构如下:

image

这样,就相当于上面第一个例子,上层建筑依赖下层建筑——每一个类的构造函数都直接调用了底层代码的构造函数。假设我们需要改动一下轮胎(Tire)类,把它的尺寸变成动态的,而不是一直都是30。我们需要这样改:

image

由于我们修改了轮胎的定义,为了让整个程序正常运行,我们需要做以下改动:

image

由此我们可以看到,仅仅是为了修改轮胎的构造函数,这种设计却需要修改整个上层所有类的构造函数!在软件工程中,这样的设计几乎是不可维护的——在实际工程项目中,有的类可能会是几千个类的底层,如果每次修改这个类,我们都要修改所有以它作为依赖的类,那软件的维护成本就太高了。

所以我们需要进行控制反转(IoC),及上层控制下层,而不是下层控制着上层。我们用依赖注入(Dependency Injection)这种方式来实现控制反转。所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。这里我们用构造方法传递的依赖注入方式重新写车类的定义:

image

这里我们再把轮胎尺寸变成动态的,同样为了让整个系统顺利运行,我们需要做如下修改:

image

只需要修改轮胎类就行了,不用修改其他任何上层类。这显然是更容易维护的代码。
如果我们要写Car类的单元测试,就只需要Mock一下Framework类传入Car就行了,而不用把Framework, Bottom, Tire全部new一遍再来构造Car。

其他:依赖倒置 DIP

高层模块(抽象模块)不应该依赖低层模块,两者都应该依赖抽象
抽象不应该依赖细节
细节应该依赖抽象

依赖注入:一个A类要依赖于注入的B类才能实现作用
依赖倒置:一个A类要依赖于 倒着注入(不限于注入)的B类才能使用,倒着注入就是反射

依赖倒置核心就是:要面向抽象去编程
类似如下代码就是依赖倒置,没有直接注入,而是接口+反射 实现了倒置注入

 public static void main(String[] args) throws Exception {
        String name=Main.getInput();
        IBase iBase= Factory.getUser(name);   
        iBase.print();
    }
上一篇 下一篇

猜你喜欢

热点阅读