乐趣区

关于java:浅谈从面向抽象编程再到IOC控制反转

1.WHAT- 面向形象编程的例子

1.1 面向对象

一般来说,面向对象编程是咱们比拟常见的,行将一个理论的我的项目分成多个类(对象),赋予这些对象属性和办法,从而实现编程。

比方,咱们要编写一个校园治理平台,别离治理老师和学生,十分直观地咱们就能把老师类和同学类给演绎进去,老师有教学的职能(办法)有性别年龄(属性),学生则有念书的职能(办法),也有性别年龄(属性)。

1.2 面向对象进阶到面向形象

在我看来,面向对象其实就是:演绎总结。咱们程序猿将产品经理提出的直观需要进行演绎总结,能够失去演绎的类。如果咱们能把演绎的类再进一步地演绎(或者说形象),咱们就失去了一个抽象类。

同一个例子,除了老师类,学生类,咱们还能够演绎一个“人”类,这个“人”类有着一个人应具备的属性(如性别,年龄,身高,体重)和能力(如吃饭,睡觉)。
(P.S:当然,演绎出一个抽象类只是面向形象的一部分,并不是面向形象的全副。)

2.WHY- 为什么要面向形象编程

2.1 代码编写

在代码编写层面,有过编程教训的我置信都会或多或少经验过:将专用的局部抽离进去,这样就不必反复编写代码。例如组件、包,咱们不须要去编写重复性极强的代码。

2.2 代码保护

在代码编写层面,更多还是面向对象的思维在起作用。针对一个我的项目,咱们失常的思维往往是自上而下的,因而先演绎出一个抽象类,再失去具体类,这是很失常的思维逻辑,这样写进去的代码,层次分明,但还是远远不够。

一个大型项目,往往难的不是它的开发,而是它的保护。大型项目里,即便咱们用抽象类的形式将类进行了演绎总结,但类与类之间依然不可避免地有着十分强的依赖关系。

举个栗子:咱们 A 类形象成 IA 类,但咱们依然要去实例化类 A,而实例化类 A 的启动类 B,仍然援用了 A,启动类 B 和 A 的依赖性依然存在。(java 的多态)

2.2.1 开闭准则(OCP)

首先,要想更好地实现代码保护,咱们就要遵循 开闭准则:容许拓展,禁止批改。

见上图,上图的四个齿轮是咱们的四个类,它们相互依赖从而推动程序的运行。但产品经理有一天忽然提出了一个新需要,这个需要可能咱们间接批改类 A 就实现了,然而类 A 一旦产生变动,咱们的类 B,类 D 也得跟着动,而后连带着 C 也可能要跟着动,这样的变动是灾难性的。

由此可见,要遵循开闭准则,并不是一件容易的事件,这必须是在设计代码框架时就得思考分明。那么到底怎么能力做到开闭准则呢?

2.2.2 面向形象编程

咱们须要明确:变动(产品经理)是诚然存在的,是不可磨灭的。代码想要做到只拓展,齐全不批改是不可能的。因而,遵循 OCP 准则,咱们应该做到的是:
1. 将代码的变动隔离至一处。
2. 升高类与类之间的关联性,让每个类能够独立变动而不影响到其余类的应用。

而面向形象编程正是将这些变动形象,将类与类关联性升高。它有三个方面:
1. 用变量抽象化“值”的变动;
2. 用 Interface 接口抽象化“类”的变动;
3.用一个容器来封装类的实例化,再用反射机制来抽象化类的实例化。

2.2.3 工厂模式

面向形象编程的前两个方面在日常编程中其实是十分罕用的,像写配置文件,此处就不再多做赘述。次要还是第三点:用容器封装类的实例化。类的实例化是类与类之间相互依赖的关键所在,正是因为一个类间接在另一个类中进行了实例化,两个类才产生耦合。


见上图,还是四个齿轮,但我这次在四个齿轮之间退出了一个大齿轮,今后不再是小齿轮相互推动运行,而是大齿轮带动四个小齿轮进行运行。不言而喻,小齿轮之间的关联性曾经被升高十分多了,之后不论是批改小齿轮的外部,或者是要更换小齿轮,都比原来的危险要小的多。

回到代码上来,这个大齿轮,其实就是一个容器,用这个容器实现类与类之间的关联。而要实现这个容器,咱们能够采纳 工厂模式 + 反射机制

用具体的代码来实现一下工厂模式 + 反射机制:
假如咱们须要做一个英雄联盟的选英雄,并开释 R 技能的简略 demo。
单纯的工厂模式:

// 主控类
public class Main {public static void main(String[] args) throws Exception {String name = Main.getPlayerInput();
      Hero hero = HeroFactory.getHero(name);
      hero.r();}

  private static String getPlayerInput(){System.out.println("Enter a Hero's Name");
      Scanner scanner = new Scanner(System.in);
      String name = scanner.nextLine();
      return name;
  }
}
// 工厂类
public class HeroFactory {public static Hero getHero(String name) throws Exception {
      Hero hero;
      switch (name){
          case "Diana":
              hero = new Diana();
              break;
          case "Irelia":
              hero = new Irelia();
              break;
          case "Camile":
              hero = new Camile();
              break;
          default:
              throw new Exception();}
      return hero;
  }
}

乍看上去,如同咱们通过形象所有英雄有了一个 Hero 类,同时用工厂来生成它,曾经很好地封装了,然而,咱们每一次增加英雄,都须要去变动这个工厂,这个工厂作为咱们的“大齿轮”,显然还不够资格,它连结了各个英雄类,间接批改它仍然有影响连结的危险。
(感觉这个例子可能说服力不太强,因为各个英雄类之间关联性自身就不大,看起来批改工厂也问题不大,我只能间接从齿轮直观来解释了,不晓得大家有无更好的例子 - -。)。

工厂模式 + 反射机制:

public static Hero getHero(String name) throws Exception {
    String classStr = "reflect.hero." + name;
    Class<?> cla = Class.forName(classStr);
    Object obj = cla.newInstance();
    return (Hero) obj;
}

单纯的工厂模式,每一次都须要的变动是因为实例化过程还未被形象,若是加上了 java 的反射机制,那就不同了,类的实例化齐全取决于 用户输出的英雄名(或者说配置文件中的英雄名)。无论咱们新增任何英雄还是要调整任何英雄,该工厂都无需发生变化,

不言而喻,这个工厂根本实现了咱们图中的“大齿轮”,同时,咱们能够看到,这样的一个大齿轮,很好地把咱们管制代码运行的类和实现业务逻辑的类实现了拆散————控制代码:MAIN+FACTORY;业务代码:各个英雄类 DIANA+IRELIA+CAMILE。

因而,咱们能将 OCP 准则换一种形式了解:尽量不要批改负责管制的代码,只批改业务逻辑的代码

3.IOC 管制反转

假如有一个 A 类须要援用 B 类,那么一般来说,咱们会在 A 类中实例化 B 类;而引入一个容器,将 A 类,B 类都当时引入容器中,再由容器把 B 类注入到 A 类中,这就实现了 IOC 管制反转。

从这个概念来看,工厂模式 + 反射机制,这种模式曾经根本实现了 IOC 管制反转(将英雄类注入到管制类之中)。然而理论状况中,容器注入某个类并非下面的例子那么简略,它可能须要依据某些特定的条件来判断哪一个状况须要注入哪一个类,然而如此繁琐的业务逻辑,天然不能放在工厂之中,因而要用到 java 的 Springboot 框架。

此处不过多做开展,待下次再记录一下对 Springboot 框架的相干了解。

本人的疑难:

1. 工厂模式 + 反射曾经解决了管制类不产生变动了,那么 Spring 框架绝对于它的劣势到底在哪呢?因为 Spring 框架还须要先将类生成 bean 退出到 IOC 容器之中,还多了这么一步,所以感觉较之工厂模式 + 反射来说还更加繁琐。

退出移动版