浅谈IOC与DI

浅谈IOC与DI,第1张

浅谈IOC与DI
  • 编程的基本思维
  • 开闭原则(OCP)
  • 面向抽象编程
  • 英雄联盟 demo 推导
    • 第一版烂大街的代码风格
    • 第二版interface抽象风格
    • 第三版工厂模式
    • 第四版反射
  • 原理结论
  • IOC
  • DI
  • DIP

编程的基本思维
  • 注重项目的维护和迭代
  • 追求可维护的代码
  • 好的代码
    • 不啰嗦的代码
    • 具有自描述性的代码
    • 具有可维护性的代码
  • 所有软件的复杂性都是为了追求可维护的代码
  • 计算机的代码其实是现实世界的一些规律或者说一些业务的映射,也就是说需要使用代码模拟现实世界的一些业务,从而解决这些业务的问题。可以把代码视作现实世界一些业务的投影
开闭原则(OCP)

开闭原则(OCP): Open Closed Principle

  • 对扩展开放,对修改封闭
  • 修改一处代码可能会引起其他地方的 bug,最好的方式就是新增业务模块/类代替原来的业务模块/类,使出现 bug 的几率变小
  • 必须满足此原则的代码才能算作好的、可维护的代码
面向抽象编程
  • 只有实现面向抽象编程,才能够逐步实现开闭原则
  • 面临的两个问题:
    • 统一方法的调用
    • 统一对象的实例化
  • 可实现面向抽象编程的语法:
    • 接口(interface)
    • 抽象类(abstract)
  • 只要有了接口和抽象类的概念,多态性才能够得到很好的支持
  • 面向抽象编程的目的: 实现可维护的代码,实现开闭原则

实现面向抽象编程:interface => 设计模式:工厂模式 => IOC/DI

实现OCP: 面向抽象 => OCP => 可维护的代码

英雄联盟 demo 推导
  • 由一个小型 demo 项目模拟英雄联盟用户选择英雄释放技能,推导 IOC 与 DI 的实现原理

  • 将分别列举四个不同的版本演进这个推导过程,达到逐步理解 IOC 和 DI 的目的

第一版烂大街的代码风格

选择英雄释放技能 main 函数

package com.moon.lol;

import com.moon.lol.awkward.hero.Camille;
import com.moon.lol.awkward.hero.Diana;
import com.moon.lol.awkward.hero.Irelia;

import java.util.Scanner;

public class Main1 {

    // 第一版:烂大街的代码风格
    public static void main(String[] args) {
        String name = Main1.getPlayerInput();
        switch (name) {
            case "Diana":
                Diana diana = new Diana();
                diana.r();
                break;
            case "Irelia":
                Irelia irelia = new Irelia();
                irelia.r();
                break;
            case "Camille":
                Camille camille = new Camille();
                camille.r();
                break;
        }
    }

    /**
     * 接收玩家的输入(玩家输入英雄名字)
     * @return
     */
    private static String getPlayerInput() {
        System.out.println("Enter a Hero's Name");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}

各英雄类

package com.moon.lol.awkward.hero;

/**
 * 戴安娜
 */
public class Diana {
    /**
     * 技能 Q
     */
    public void q() {
        System.out.println("Diana Q");
    }

    /**
     * 技能 W
     */
    public void w() {
        System.out.println("Diana W");
    }

    /**
     * 技能 E
     */
    public void e() {
        System.out.println("Diana E");
    }

    /**
     * 技能 R
     */
    public void r() {
        System.out.println("Diana R");
    }
}
package com.moon.lol.awkward.hero;

/**
 * 艾瑞莉娅(刀妹)
 */
public class Irelia {
    /**
     * 技能 Q
     */
    public void q() {
        System.out.println("Irelia Q");
    }

    /**
     * 技能 W
     */
    public void w() {
        System.out.println("Irelia W");
    }

    /**
     * 技能 E
     */
    public void e() {
        System.out.println("Irelia E");
    }

    /**
     * 技能 R
     */
    public void r() {
        System.out.println("Irelia R");
    }
}
package com.moon.lol.awkward.hero;

/**
 * 卡蜜尔(青钢影)
 */
public class Camille {
    /**
     * 技能 Q
     */
    public void q() {
        System.out.println("Camille Q");
    }

    /**
     * 技能 W
     */
    public void w() {
        System.out.println("Camille W");
    }

    /**
     * 技能 E
     */
    public void e() {
        System.out.println("Camille E");
    }

    /**
     * 技能 R
     */
    public void r() {
        System.out.println("Camille R");
    }
}
第二版interface抽象风格

选择英雄释放技能 main 函数

package com.moon.lol;

import com.moon.lol.abstraction.ISkill;
import com.moon.lol.abstraction.hero.Camille;
import com.moon.lol.abstraction.hero.Diana;
import com.moon.lol.abstraction.hero.Irelia;

import java.util.Scanner;

public class Main2 {

    /**
     * 第二版:interface抽象风格
     * 单纯interface可以统一方法的调用,但是它不能统一对象的实例化
     */
    public static void main(String[] args) throws Exception {
        String name = Main2.getPlayerInput();
        ISkill iSkill;
        switch (name) {
            case "Diana":
                iSkill = new Diana();
                break;
            case "Irelia":
                iSkill = new Irelia();
                break;
            case "Camille":
                iSkill = new Camille();
                break;
            default:
                throw new Exception();
        }
        iSkill.r();
    }

    /**
     * 接收玩家的输入(玩家输入英雄名字)
     * @return
     */
    private static String getPlayerInput() {
        System.out.println("Enter a Hero's Name");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}

英雄技能接口类

package com.moon.lol.abstraction;

/**
 * 技能接口 Q W E R
 */
public interface ISkill {
    /**
     * 技能 Q
     */
    void q();

    /**
     * 技能 W
     */
    void w();

    /**
     * 技能 E
     */
    void e();

    /**
     * 技能 R
     */
    void r();
}

各英雄类

package com.moon.lol.abstraction.hero;

import com.moon.lol.abstraction.ISkill;

/**
 * 戴安娜
 */
public class Diana implements ISkill {
    /**
     * 技能 Q
     */
    public void q() {
        System.out.println("Diana Q");
    }

    /**
     * 技能 W
     */
    public void w() {
        System.out.println("Diana W");
    }

    /**
     * 技能 E
     */
    public void e() {
        System.out.println("Diana E");
    }

    /**
     * 技能 R
     */
    public void r() {
        System.out.println("Diana R");
    }
}
package com.moon.lol.abstraction.hero;

import com.moon.lol.abstraction.ISkill;

/**
 * 艾瑞莉娅(刀妹)
 */
public class Irelia implements ISkill {
    /**
     * 技能 Q
     */
    public void q() {
        System.out.println("Irelia Q");
    }

    /**
     * 技能 W
     */
    public void w() {
        System.out.println("Irelia W");
    }

    /**
     * 技能 E
     */
    public void e() {
        System.out.println("Irelia E");
    }

    /**
     * 技能 R
     */
    public void r() {
        System.out.println("Irelia R");
    }
}
package com.moon.lol.abstraction.hero;

import com.moon.lol.abstraction.ISkill;

/**
 * 艾瑞莉娅(刀妹)
 */
public class Irelia implements ISkill {
    /**
     * 技能 Q
     */
    public void q() {
        System.out.println("Irelia Q");
    }

    /**
     * 技能 W
     */
    public void w() {
        System.out.println("Irelia W");
    }

    /**
     * 技能 E
     */
    public void e() {
        System.out.println("Irelia E");
    }

    /**
     * 技能 R
     */
    public void r() {
        System.out.println("Irelia R");
    }
}
第三版工厂模式

选择英雄释放技能 main 函数

package com.moon.lol;

import com.moon.lol.factory.HeroFactory;
import com.moon.lol.factory.ISkill;

import java.util.Scanner;

public class Main3 {
    /**
     * 第三版:工厂模式分离对象实例化
     * 把对象实例化的过程,转移到其他的代码片段里
     */
    public static void main(String[] args) throws Exception {
        String name = Main3.getPlayerInput();
        ISkill iSkill = HeroFactory.getHero(name);
        iSkill.r();
    }

    /**
     * 接收玩家的输入(玩家输入英雄名字)
     * @return
     */
    private static String getPlayerInput() {
        System.out.println("Enter a Hero's Name");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}

生产英雄的工厂类

package com.moon.lol.factory;

import com.moon.lol.factory.hero.Camille;
import com.moon.lol.factory.hero.Diana;
import com.moon.lol.factory.hero.Irelia;

/**
 * 简单工厂模式
 * 生产或实例化英雄类,把对象实例化的过程隔离
 */
public class HeroFactory {
    public static ISkill getHero(String name) throws Exception {
        ISkill iSkill;
        switch (name) {
            case "Diana":
                iSkill = new Diana();
                break;
            case "Irelia":
                iSkill = new Irelia();
                break;
            case "Camille":
                iSkill = new Camille();
                break;
            default:
                throw new Exception();
        }
        return iSkill;
    }
}

ISkill 接口、Diana、Irelia、Camille 类与第二版基本相同

第四版反射

英雄工厂类

package com.moon.lol.reflect;

/**
 * 简单工厂模式
 */
public class HeroFactory {
    /**
     * 第四版:通过反射机制消除所有的变化
     * 正向思维:工厂模式 + 反射并不是 IOC 和 DI
     */
    public static ISkill getHero(String name) throws Exception {
        // 元类、反射
        // 对象 类 元类
        String classStr = "com.moon.lol.reflect.hero." + name;
        Class<?> cla = Class.forName(classStr);
        Object obj = cla.newInstance();
        return (ISkill) obj;
    }
}

Main 类与第三版基本相同

ISkill 接口、Diana、Irelia、Camille 类与第二版基本相同

原理结论
  1. 单纯 interface 可以统一方法的调用,但是它不能统一对象的实例化
  2. 面向对象主要做两件事情:实例化对象 调用方法(完成业务逻辑)
  3. 只有一段代码中没有 new 的出现,才能保持代码的相对稳定,才能逐步实现 OCP
  4. 上面的这句话只是表象,实质是一段代码如果要保持稳定,就不应该负责对象的实例化
  5. 对象实例化是不可能消除的
  6. 把对象实例化的过程,转移到其他的代码片段里
  7. 代码中总是会存在不稳定,隔离这些不稳定,保证其他的代码是稳定的
  8. 变化造成了不稳定
  9. 配置文件属于系统外部的,而不属于代码本身
IOC

在平时的Java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象或者依赖对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题,A得到Spring给我们的对象B之后,两个人一起协作完成要完成的工作即可。

IOC实现:容器 加入容器 注入

抽象意义:控制权交给用户

  • Inversion of Control 控制反转
  • 它不是一门技术而是一种设计思想
  • 利用 IoC 将你设计好的对象交给容器控制,而非传统地在你的对象内直接控制、处理
  • IOC很好的体现了面向对象设计法则之一 —— 好莱坞法则:“别找我们,我们找你”;即由IOC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
  • 控制反转IOC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IOC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IOC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IOC容器了,通过IOC容器来建立它们之间的关系。
DI
  • Dependency Injection 依赖注入
  • DI 其实也不是一门技术,它是一种实现 IoC 的方式
  • 注入形式:
    • 属性注入
    • 构造注入
    • 接口注入
DIP
  • Dependency Inversion Principle 依赖倒置
  • 高层模块不应该依赖低层模块,两者都应该依赖抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/736893.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存