读书人

Spring温故知新(2) IoC控制反转与DI

发布时间: 2012-12-22 12:05:05 作者: rapoo

Spring温故知新(二) IoC控制反转与DI依赖注入(修正1)
学习Spring,所有教程都是从IoC和DI开始的,但是大部分教程对它们的介绍都很抽象,要是之前没系统的学习过设计模式的话理解起来就非常吃力。所以在这里我尽我的能力来解释这两个概念。

首先的重点,IoC并不是Spring特有的,它是一种设计模式,事实上只要掌握了原理,自己也可以写出一个IoC的实现方法。

一、DI(Dependency Injection)依赖注入可以称为IoC(Inversion of Control)控制反转,但是IoC不等于就是DI
这一点是很多初学者很容易产生错误的一个概念,因为很多教程都把DI和IoC放在一起说,就像堆和堆栈一样,容易让初学者以为他们就是同一个东西,这个是需要纠正的。
简单的解释,就是IoC包括了DI,但是IoC还包括了另外一个叫做DL(Dependency Lookup)依赖查找的功能。你可以先简单的把IoC当作是某种操作,DI是这种操作的写入方式,而DL是查找方式,这样可能容易区分一点。
DL之所以很少有人提起,是因为我们平时很少用到,所以也就慢慢被遗忘了,但是这也不代表它就不存在了。可惜我自己也对DL没有去花时间了解,只是知道有这么个东西,所以在这暂时也没法去详细介绍了,如果有高手的话请指点一下!

二、什么叫做IoC(Inversion of Control)控制反转
这一段是纯理论的介绍,从别的地方直接抄过来的,先看一遍,不懂也没关系,下面我会举例来说明:
package com.iteye.bolide74.action;public class Robot {public String name;public String color;public double height;public double width;public double weight;public Robot(String name, String color, double height, double width,double weight) {this.name = name;this.color = color;this.height = height;this.width = width;this.weight = weight;}public void Speak(String msg) {System.out.println(msg);}}
实现类:

package com.iteye.bolide74.tester;import com.iteye.bolide74.action.Robot;public class IoCTester {public static void main(String[] args) {Robot robot0 = new Robot("robot0", "black", 80.000, 40.0000, 1000.0000);robot0.Speak("Hello,World!");}}


优点:方便简单啊!随便学个一天JAVA就会用啊!
缺点:写代码倒是方便了,但是以后维护起来就超级麻烦。既然是机器人那么制造的时候势必还会涉及它的名字、颜色、高矮胖瘦之类的参数。假设我要造N个黑色的机器人去给一楼的邻居打招呼;白色的去二楼;红色的去三楼等等。如果某天发现红色的油漆没了需要换成蓝色,那岂不是得翻出每个红色机器人的实现类,然后一个个的去改掉这个机器人实例的颜色? 太累了!


2、使用简单工厂和静态工厂模式来创建机器人:
用简单工厂模式来创建,那就相对简单方便一些了,我们会在工厂类里事先写好一些常用类型的机器人,然后只要用机器人的编号来获得相应的机器人就行了。
还是调用那个Robot类不变,只改实现类:
package com.iteye.bolide74.tester;import com.iteye.bolide74.action.Robot;class RobotFactory {public Robot getRobot(int robotType) {                Robot robot;switch (robotType) {case 0:robot = new Robot("robot0", "black", 80.000, 40.0000, 1000.0000);break;case 1:robot = new Robot("robot1", "white", 90.000, 30.0000, 800.0000);break;default:robot = new Robot("defaultRobot", "red", 10.000, 10.0000, 300.0000);break;}return robot;}}public class IoCTester {public static void main(String[] args) {RobotFactory robotFactory = new RobotFactory();Robot robot = robotFactory.getRobot(0);robot.Speak("Hello,World! My name is " + robot.name);}}

这样的话后期维护就方便很多了,当有一天需要把已经造好的所有红色机器人换成蓝色,那就只要在getRobot方法里相应的new Robot()里的red参数换成blue就行了。简单么?
但是我觉得为了这么一个单一的类多写一个工厂太麻烦了,有没有办法省略这个工厂呢?答案是肯定的,我们可以直接把getRobot这个获取实例的方法写在Robot类里面就不需要新建专门的工厂类了,这里新建一个Robot2类来与原先的Robot类区分开:
package com.iteye.bolide74.action;public class Robot2 {public String name;public String color;public double height;public double width;public double weight;private Robot2(String name, String color, double height, double width,double weight) {this.name = name;this.color = color;this.height = height;this.width = width;this.weight = weight;}public static Robot2 getRobot(int robotType) {Robot2 robot2;switch (robotType) {case 0:robot2 = new Robot2("robot0", "black", 80.000, 40.0000, 1000.0000);break;case 1:robot2 = new Robot2("robot1", "white", 90.000, 30.0000, 800.0000);break;default:robot2 = new Robot2("defaultRobot", "red", 10.000, 10.0000, 300.0000);break;}return robot2;}public void Speak(String msg) {System.out.println(msg + ",我是" + this.name);}}

实现类
package com.iteye.bolide74.tester;import com.iteye.bolide74.action.Robot2;public class IoCTester {public static void main(String[] args) {Robot2 robot2 = Robot2.getRobot(1);robot2.Speak("Hello,world!");}}

以上就是静态工厂的实现方式了!
注意!这里有两个细节:
1) Robot2的构造方法是private的,这样的好处就是所有Robot2的使用者或者说是实现类在获取机器人的时候,只能通过getRobot2来获取,这样就实现了规范化,而不是有些地方是直接new的,有些地方是用getRobot方法来的(一个项目,不一定就是一个程序员写到老的...)。
2) Robot2获取实例的getRobot方法是static静态的(所以才叫静态工厂嘛~)!由于Robot2的构造函数是private的了,不能直接new一个实例,那就更加不可能调用需要实例化才能使用的getRobot方法了,所以必须要设置为static方法,就可以无需实例化直接调用!
现在用了以上两种工厂方法,只要在需要机器人的时候在getRobot(int robotType)里设进想要的机器人的型号,机器人工厂就会自动送过来我想要的机器人了!

不过现在又出现了一种状况,就是我的GF正好在家,那么我就不用这么麻烦的造机器人了,我只要叫我女朋友去招呼就行了。(TNND就这种宅男+使唤癖,能有GF就有鬼了!)
那么再用这个简单工厂和静态工厂模式就不适用了,毕竟GF不是机器人(我看以这人的脾气其实很有可能GF也是机器人...),那么就得再写一个People类,然后再在这个类里实现Speak方法,然后再新建一个Girlfriend实例来实现speak方法打招呼。你也觉得这样太累了吧?毕竟People类和Robot类都是用来Speak而已,要是来回切换不同的类去Speak的话就得重复建立不同的实例和不同的Speak方法,还是很麻烦!


3、抽象工厂模式(误)多亏yelinsen05的指正,我再查了一下相关资料,这第三种方式其实还是简单工厂模式,误人子弟了...sorry!关于真正的抽象工厂模式,我下回再介绍。学艺不精,献丑了....
真正的抽象工厂介绍请移步看我另外一篇博文:http://bolide74.iteye.com/blog/1001900


现在为了解决简单工厂和静态工厂模式的缺陷,我们可以把Speak这个方法(或者可以称为功能模块)抽取出来,作为一个接口来实现,这样只要People类和Robot类都实现这个接口,就能够通用了。
package com.iteye.bolide74.impl;public interface ISpeaker {public void Speak(String msg);}

接下来是新的Robot类和People类,它们都同时实现了ISpeaker这个接口,两者都用了不同的Speak内容来区分人类和机器人说话方式的不同:
package com.iteye.bolide74.action;import com.iteye.bolide74.impl.ISpeaker;public class Robot implements ISpeaker {public String name;public String color;public double height;public double width;public double weight;public Robot(String name, String color, double height, double width,double weight) {this.name = name;this.color = color;this.height = height;this.width = width;this.weight = weight;}@Overridepublic void Speak(String msg) {System.out.println(msg + ",我是" + this.name + ",我的体重为" + this.weight);}}
package com.iteye.bolide74.action;import com.iteye.bolide74.impl.ISpeaker;public class People implements ISpeaker {public String name;public double height;public double weight;public People(String name, double height, double weight) {this.name = name;this.height = height;this.weight = weight;}@Overridepublic void Speak(String msg) {System.out.println(msg + ",我是" + this.name + ",我就不告诉你我有多重!");}}

接下来就是工厂类和实现类了。这个工厂类就不是生产具体的实例对象了,而是生产一个通用的能够Speak的抽象实例(某个能打招呼的东西,或许是人类,或许是机器人)
package com.iteye.bolide74.tester;import com.iteye.bolide74.action.People;import com.iteye.bolide74.action.Robot;import com.iteye.bolide74.impl.ISpeaker;class SpeakerFactory {public ISpeaker getSpeaker(int speakerType) {ISpeaker speaker;switch (speakerType) {case 0:speaker = new Robot("robot0", "black", 80.000, 40.0000, 1000.0000);break;case 1:speaker = new People("GirlFriend", 1.64, 99.99);break;default:speaker = new Robot("defaultRobot", "red", 10.000, 10.0000,300.0000);break;}return speaker;}}public class IoCTester {public static void main(String[] args) {SpeakerFactory speakerFactory = new SpeakerFactory();ISpeaker speaker = speakerFactory.getSpeaker(1);speaker.Speak("Hello,World!");}}

输出结果:

总算是有人响应了一下... 多谢支持! 这一篇博文花了我接近10个小时的时间,发现写博文还真是不容易。不过有您的支持这一切都值得了!
总算是有人响应了一下... 多谢支持! 这一篇博文花了我接近10个小时的时间,发现写博文还真是不容易。不过有您的支持这一切都值得了!
哈哈 不客气啦 这种原创文章好难得的 至少市面上卖的那些教程书都不会说这么详细生动 期待持续。。。。。。。。。 对我理解设计模式有很大好处,由浅入深,彪悍的文章不需要解释!

读书人网 >编程

热点推荐