java的IOC和DI

开闭原则(OCP)Open Closed Principle

对于软件、函数和类而言,修改时封闭的,扩展是开放的。

例如:上一节讲到的接口,为什么要创建一个v1和v2版本,因为修改时封闭的,扩展时开放的;例如面向对象中,类的封装和扩展

IOC 【控制反转】和DI【依赖注入】 的目的也是为了实现开闭原则。

面向抽象编程可以实现OCP,为了编写可维护代码

1、interface【接口】、abstract【抽象类】,多态性。

2、设计模式:工厂模式

3、IOC和DI

面向对象:1、实例化对象 2、调用方法(完成业务逻辑)

引入英雄联盟英雄小案例

demo1:面向对象编程,缺点明显,笨重。

####创建package:akwward.hero,在下面分别创建3个类
####类1:Camille
package com.company.awkward.hero;
public class Camille {
    public void q(){
        System.out.println("Camille Q");
    }
    public void w(){
        System.out.println("Camille W");
    }
}
####类2:Diana
package com.company.awkward.hero;
public class Fox {
    public void q(){
        System.out.println("Fox Q");
    }
    public void w(){
        System.out.println("Fox W");
    }
}
####类3:Fox
package com.company.awkward.hero;
public class Fox {
    public void q(){
        System.out.println("Fox Q");
    }
    public void w(){
        System.out.println("Fox W");
    }
}
####main的方法
package com.company;

import com.company.awkward.hero.Camille;
import com.company.awkward.hero.Diana;
import com.company.awkward.hero.Fox;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
	// write your code here
        String HeroName = Main.getPlayerInput();
        switch (HeroName){
            case "Diana":
                Diana diana = new Diana();
                diana.q();
                break;
            case "Camille":
                Camille camille = new Camille();
                camille.q();
                break;
            case "Fox":
                Fox fox = new Fox();
                fox.q();
                break;
        }

    }
    private static String getPlayerInput(){
        System.out.println("Enter a Hero's Name:");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}
JAVA、基础技术、技术与框架java的IOC和DI插图

改造思路V2:引入interface

例子说明:

demo2:单纯interface可以统一方法的调用,但它不能统一对象的实例化。

例如对比demo1,把方法的调用抽离了。

####创建package:abstraction.hero,在下面分别创建3个类
####创建一个interface在package:abstraction
###ISkill
package com.company.abstraction;

public interface ISkill {
    void q();
    void w();
}

###类1:CamilleImpl
package com.company.abstraction.hero;

import com.company.abstraction.ISkill;

public class CamilleImpl implements ISkill {
    public void q(){ System.out.println("Camille Q"); }
    public void w(){ System.out.println("Camille W"); }
}
###类2:CamilleImpl
package com.company.abstraction.hero;

import com.company.abstraction.ISkill;

public class DianaImpl implements ISkill {
    public void q(){ System.out.println("Diana Q"); }
    public void w(){ System.out.println("Diana W"); }
}
###类3:FoxImpl
package com.company.abstraction.hero;

import com.company.abstraction.ISkill;

/* 类实现接口 规范化类的名字后面追加Impl*/
public class FoxImpl implements ISkill {
    public void q(){ System.out.println("Fox Q"); }
    public void w(){ System.out.println("Fox W"); }
}

###Main
package com.company;

import com.company.abstraction.ISkill;
import com.company.abstraction.hero.CamilleImpl;
import com.company.abstraction.hero.DianaImpl;
import com.company.abstraction.hero.FoxImpl;
import com.company.awkward.hero.Camille;
import com.company.awkward.hero.Diana;
import com.company.awkward.hero.Fox;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        String HeroName = Main.getPlayerInput();
        ISkill iSkill;
        switch (HeroName){
            case "Diana":
                iSkill = new DianaImpl();
                break;
            case "Camille":
                iSkill = new CamilleImpl();
                break;
            case "Fox":
                iSkill = new FoxImpl();
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + HeroName);
        }
        iSkill.q();

    }

    private static String getPlayerInput(){
        System.out.println("Enter a Hero's Name:");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}
JAVA、基础技术、技术与框架java的IOC和DI插图1

改造思路V3:如果代码中没有new实例化的出现,就能保持代码相对稳定,逐步实现OCP

如果一段代码要保持稳定,就不应该负责对象的实例化。由于对象实例化不能取消,只能转移实例化。

demo3:在interface基础上引入工厂模式

工厂模式分三种

  • 简单工厂模式
  • 工厂模式
  • 抽象工厂模式
####创建package:factory.hero,在下面分别创建3个类
####创建一个interface在package:factory
###ISkill
package com.company.factory;

public interface ISkill {
    void q();
    void w();
}

###类1:CamilleImpl
package com.company.factory.hero;

import com.company.factory.ISkill;

public class CamilleImpl implements ISkill {
    public void q(){ System.out.println("Camille Q"); }
    public void w(){ System.out.println("Camille W"); }
}
###类2:CamilleImpl
package com.company.factory.hero;

import com.company.factory.ISkill;

public class DianaImpl implements ISkill {
    public void q(){ System.out.println("Diana Q"); }
    public void w(){ System.out.println("Diana W"); }
}

###类3:FoxImpl
package com.company.factory.hero;

import com.company.factory.ISkill;

/* 类实现接口 规范化类的名字后面追加Impl*/
public class FoxImpl implements ISkill {
    public void q(){ System.out.println("Fox Q"); }
    public void w(){ System.out.println("Fox W"); }
}
####创建一个类在package:factory,HeroFactory
####HeroFactory
package com.company.factory.hero;

import com.company.factory.ISkill;

public class HeroFactory {
    public static ISkill getHero(String HeroName){
        ISkill iSkill;
        switch (HeroName){
            case "Diana":
                iSkill = new DianaImpl();
                break;
            case "Camille":
                iSkill = new CamilleImpl();
                break;
            case "Fox":
                iSkill = new FoxImpl();
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + HeroName);
        }

        return iSkill;
    }
}


###Main
package com.company;

import com.company.factory.ISkill;
import com.company.factory.hero.CamilleImpl;
import com.company.abstraction.hero.DianaImpl;
import com.company.abstraction.hero.FoxImpl;
import com.company.factory.hero.HeroFactory;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        String HeroName = Main.getPlayerInput();
        /* import ISkill 的目录要改成 factory*/
        ISkill iSkill = HeroFactory.getHero(HeroName);
        iSkill.q();

    }
    private static String getPlayerInput(){
        System.out.println("Enter a Hero's Name:");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}

本次改进,使用了简单工厂模式,对比起demo2,把main方法中的实例化迁移至HeroFactory。此时的main方法就不受类的增多或改变而影响。

不过此方法仍然不是最佳,因为在HeroFactory依然需要实例化。如何改进呢?

domo4:使用反射,用户属于名字就可以实例化。

这里就使用到JDK8的newInstance()

如果JDK11等,clazz.getDeclaredContructor().newInstance()

####创建package:reflect.hero,在下面分别创建3个类
####创建一个interface在package:reflect
###ISkill
package com.company.reflect;

public interface ISkill {
    void q();
    void w();
}

###类1:CamilleImpl
package com.company.reflect.hero;

import com.company.reflect.ISkill;

public class CamilleImpl implements ISkill {
    public void q(){ System.out.println("Camille Q"); }
    public void w(){ System.out.println("Camille W"); }
}
###类2:CamilleImpl
package com.company.reflect.hero;

import com.company.reflect.ISkill;

public class DianaImpl implements ISkill {
    public void q(){ System.out.println("Diana Q"); }
    public void w(){ System.out.println("Diana W"); }
}

###类3:FoxImpl
package com.company.reflect.hero;

import com.company.reflect.ISkill;

/* 类实现接口 规范化类的名字后面追加Impl*/
public class FoxImpl implements ISkill {
    public void q(){ System.out.println("Fox Q"); }
    public void w(){ System.out.println("Fox W"); }
}

####创建一个类在package:reflect,HeroFactory
####HeroFactory
package com.company.reflect.hero;

import com.company.reflect.ISkill;

public class HeroFactory {
    public static ISkill getHero(String HeroName) {
        String classStr = String.format("reflect.hero.%s",HeroName);
        Class<?> cla = null;
        try {
            cla = Class.forName(classStr);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Object obj=new Object();
        try {
            obj = cla.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return (ISkill)obj;
    }
}


###Main
package com.company;

import com.company.reflect.ISkill;
import com.company.factory.hero.CamilleImpl;
import com.company.abstraction.hero.DianaImpl;
import com.company.abstraction.hero.FoxImpl;
import com.company.reflect.hero.HeroFactory;

import java.util.Scanner;

public class Main {
    /* demo3~domo4 main方法*/
    public static void main(String[] args) {
        String HeroName = Main.getPlayerInput();
        /* import ISkill 的目录要改成 factory*/
        ISkill iSkill = HeroFactory.getHero(HeroName);
        iSkill.q();

    }
    private static String getPlayerInput(){
        System.out.println("Enter a Hero's Name:");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}

使用Interface+工厂模式+反射模式,可以让代码变得稳定;但并应用 IOC 【控制反转】和DI【依赖注入】的原理。

像上述的例子,如果我们的工厂模式是一个大工厂,可以给我们所需要对象,那么是否就可以实现理想化的IOC呢?

Spring的IOC跟上面的逻辑类似,引入了容器(类似一个大工厂),用户第二次输入的时候,从缓存容器,提升性能

关于 DIP( 依赖倒置 )、IOC和DI的介绍

DIP:抽象不应该依赖具体的实例/方法。细节应该依赖抽象

例如demo3和demo4,把main方法是通过依赖抽象【工厂模式+interface】

DI的简介【依赖注入】: 由以前主要像容器要对象,如(HeroFactory.getHero或new 实例化),改成容器提供对象(this.getHero)

  • 属性注入
  • 构造注入
  • 接口注入

引入容器后,类与类之间没有依赖关系,让系统变得更加稳定。

IOC的简介:DI是IOC的一种实现/应用

IOC的思想:main方法中,直接就可以使用这个对象,而不用实例化。 例如DI【依赖注入】由容器提供对象

控制反转:

从代码的角度看,原本在具体方法(主)实例化后,调用对象(从)改变成由容器提供对象给具体的方法。

从业务的角度看,由原来程序员决定具体功能,改成产品经理改变具体的功能。因为程序员只能新增类提供给具体方法即可。

如这上面的例子(demo1~demo3):如果没有实现IOC的话,新增英雄,除了新建英雄的类,还要在具体方法中修改。

引入了IOC后,主动权交了产品经理,程序员只需要新增英雄的类即可