# IoC
# 问题—程序间耦合
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
Bean:计算机英语中,有可重用组件的含义
JavaBean:用Java语言编写的可重用组件。JavaBean>实体类
实际开发中,应做到:编译期不依赖,运行时才依赖(想到多态了吧,只是类似)
解耦的思路(工厂模式):
- 读取配置文件的key获取要创建对象的全限定类名value;
- 使用反射创建对象,避免使用new关键字;
- 定义一个map容器,在静态代码块中创建所有对象并存放。获取对象时直接返回对应key的value,是单例的
# OCP—开闭原则
# awkward 版
/**
* 青冈影
*/
public class Camille {
public void q(){
System.out.println("Camille Q");
}
public void w(){
System.out.println("Camille W");
}
public void e(){
System.out.println("Camille E");
}
public void r(){
System.out.println("Camille R");
}
}
/**
* 黛安娜
*/
public class Diana {
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println("Diana R");
}
}
public class Main {
public static void main(String[] args) {
String heroName = getPlayerInput();
switch (heroName){
case "Diana":
Diana diana = new Diana();
diana.r();
break;
case "Irilia":
Irilia irilia = new Irilia();
irilia.r();
break;
case "Camille":
Camille camille = new Camille();
camille.r();
break;
default:
break;
}
// 每次根据用户输入 new 新对象,并用该对象调用技能方法
}
private static String getPlayerInput(){
System.out.print("Enter a hero's name: ");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
# abstraction 版
public interface ISkill {
void q();
void w();
void e();
void r();
}
/**
* 青冈影
*/
public class Camille implements ISkill {
public void q(){
System.out.println("Camille Q");
}
public void w(){
System.out.println("Camille W");
}
public void e(){
System.out.println("Camille E");
}
public void r(){
System.out.println("Camille R");
}
}
/**
* 黛安娜
*/
public class Diana implements ISkill {
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println("Diana R");
}
}
public class Main {
public static void main(String[] args) {
String heroName = getPlayerInput();
ISkill iSkill;
// 对象实例化
switch (heroName){
case "Diana":
iSkill = new Diana();
break;
case "Irilia":
iSkill = new Irilia();
break;
case "Camille":
iSkill = new Camille();
break;
default:
throw new RuntimeException();
}
// 面向对象:实例化对象,调用方法(完成业务逻辑)
// 单纯的 Interface 只能统一方法的调用,不能统一对象的实例化。即多态好处:运行时确定要调用的方法
iSkill.r();
}
private static String getPlayerInput(){
System.out.print("Enter a hero's name: ");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
# factory 版
public interface ISkill {
void q();
void w();
void e();
void r();
}
/**
* 青冈影
*/
public class Camille implements ISkill {
public void q(){
System.out.println("Camille Q");
}
public void w(){
System.out.println("Camille W");
}
public void e(){
System.out.println("Camille E");
}
public void r(){
System.out.println("Camille R");
}
}
/**
* 黛安娜
*/
public class Diana implements ISkill {
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println("Diana R");
}
}
public class HeroFactory {
public static ISkill getHero(String heroName){
ISkill iSkill;
// 对象实例化
switch (heroName){
case "Diana":
iSkill = new Diana();
break;
case "Irilia":
iSkill = new Irilia();
break;
case "Camille":
iSkill = new Camille();
break;
default:
throw new RuntimeException();
}
return iSkill;
}
}
public class Main {
public static void main(String[] args) {
String heroName = getPlayerInput();
// HeroFactory 如何消除?
// 此时是静态调用,在工厂类中实例化时还是 new 操作,并且强耦合了,新增时还需修改工厂类
ISkill iSkill = HeroFactory.getHero(heroName);
iSkill.r();
}
private static String getPlayerInput(){
System.out.print("Enter a hero's name: ");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
# reflect 版
public interface ISkill {
void q();
void w();
void e();
void r();
}
/**
* 青冈影
*/
public class Camille implements ISkill {
public void q(){
System.out.println("Camille Q");
}
public void w(){
System.out.println("Camille W");
}
public void e(){
System.out.println("Camille E");
}
public void r(){
System.out.println("Camille R");
}
}
/**
* 黛安娜
*/
public class Diana implements ISkill {
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println("Diana R");
}
}
public class HeroFactory {
public static ISkill getHero(Class<? extends ISkill> clazz) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 对象实例化
Constructor<? extends ISkill> constructor = clazz.getDeclaredConstructor();
return constructor.newInstance();
}
}
public class Main {
public static void main(String[] args) throws
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
InvocationTargetException {
ISkill hero = HeroFactory.getHero(Irilia.class);
hero.r();
}
}
# 接口 + 工厂实现 OCP 🔥
面向对象:实例化对象,调用方法(完成业务逻辑)
单纯的 Interface 只能统一方法的调用,不能统一对象的实例化。即多态好处:运行时确定要调用的方法
只有一段代码中没有 new 出现,才能保持代码的相对稳定,才能逐步实现 OCP。但是代码中总会存在不稳定,需要隔离这些不稳定因素,保证其他的代码是稳定的。即对象的实例化应该和其他分开!
不稳定表面看是由于 new 实例化对象,其实是**用户的输入(变化)**导致需要频繁修改 new 实例化
使用简单工厂模式后,该工厂获取 Bean 的方法是静态的,虽然看起来没有 new 对象,但是其实是依赖了具体实现。且需要不同类型的 Bean 时总是需要新建一个工厂类,同一类型的不同 Bean 也需要修改工厂类
此时可以使用抽象工厂模式,当然它也有类似的问题
只有这个工厂特别大,可以获得所有 Bean 时,才认为它相对稳定。如 IoC 容器,特别是 Spring 的 ApplicationContext 等接口容器
但是!工厂模式 + 反射并不是 IoC 和 DI
# 如何理解 IoC、DI 、DIP 🔥
# IoC
该概念的由来是 Java 社区的轻量级容器能够帮助开发者将来自不同项目的组件(或服务,理解即可)装配(组合)成一个内聚的项目(应用),而这种操作就是 IoC,它决定了这些容器进行组件装配的方式
IoC 的主要实现的作用就是将组件(Bean)注册到到容器中,并能给使用者提供自动注入功能
控制反转,把创建对象的权利交给容器(或工厂)。但其实需要配合接口(抽象)才能算将其使用更好。
解决了程序和对象之间的耦合,其具体实现就是 DI
# DI(Dependency Injection)
DI(依赖注入) 的目的在于将组件的配置与使用分离开。如何在运行时(不是编译时)将组件(抽象的具体实现可能有多个,所以编译时无法确定哪个)动态连入程序中(这里是不是想起接口的概念了)
这里的组件可以指代服务(Service也行)
即调用类对某一接口实现类的依赖关系由第三方容器或协作类注入,移除调用类对某一接口实现类的依赖。
一般完成特定的业务逻辑可能会需要多个类之间进行协作。按传统的做法,每个对象负责管理与自己互相协作的对象(它所依赖的对象)的引用,这会导致高度耦合并难以测试的代码。利用依赖注入,每个对象可以直接获取所依赖的对象
最开始的 new 对象,强依赖;后来使用工厂,要对象,弱依赖;最后使用 IoC 和 DI,IoC 容器注入对象(被动)。⚙️
- 属性注入(set)
- 构造注入
- 接口注入
# DIP(Dependency Inversion Principle)
依赖倒置原则
- 高层模块(抽象)不该依赖低层模块(实现),两者都应该依赖抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
总之就是说要依赖抽象。比如依赖的 Service 不应该是具体类,而应该是接口(注入的只能是具体类,理解即可)
@Autowired
private UserService userService;
此时使用的是类型注入,依赖的是 UserService 接口,IoC 容器会在运行时将该类型的具体对象注入!
← 概述 容器 & 注册组件 & 注入组件 →