JAVA菜鸟学习日记——简单代码实现IOC控制反转

题记:纯菜鸟,也在慢慢学习中。博客更主要是为了博主自己日后回顾复习,当然也欢迎大家指正。


原来校招时候,面试各种JAVA后台的时候刷过一道题——


思考:写段代码实现控制反转

当时的感觉自然是一脸懵逼——额,我连题目都闹不明白呢,咋实现呢?


先说控制反转,面试的时候我应该会blabla地扯一大堆:

【我是答案,不标准但差不多是这个意思】所谓控制反转,就是由其他外部实体将原来控制主体需要的成员等信息生成好提供给他,而不再是简单地去持有依赖对象的引用。主体对原来的内部对象不再由其静态地了解,而是由外部实体将依赖对象注入或者说提供给这个主体。显然,主体只需要自己需要什么类型的成员就可以了,至于具体依赖的是谁,不需要自己知道。 为什么这么做呢?直观地一点就是充分解耦,注入的对象的生命周期不再由控制主体决定了。

当然解耦不是说没耦合,你毕竟用到了其余的对象,还是相互联系的,只是这种静态关系变成动态的了,因为是函数运行时候注入的嘛,原来可是没关系的。另一点就是更加利于模块化调试了。你用不到这个依赖对象的时候,依赖对象实现的代码理论上随便改,可以热插拔,具体用到的时候再去提供给他就好。


学习完了这周我易的课程之后,根据老师所讲的“控制反转”一节有了一点思路和新的认识:
核心思想在于正常控制的主体(假设是A)不具有具体实例。
不需要知道实际上选哪个实现实例去做,它只是知道 我有一个什么类型的实例,具体是谁无所谓啦~

比如,正常来说,主体A要实现一个功能,实现的应该是A.doSomething();
但是现在很遗憾,有这么两种情况:
1.A要做的事情那可多的去了,doSomething()显然不能满足所有要求
2.A不一定要被用来做什么,显然一个(或者说一种)doSomething()需要采用接口方式发放出去实现。

所以,解决思路就是采取控制反转——具体实现我不管,我交给外部去实现,具体用的时候你把实体给我就好。
于是,我们做来个两件事情:
1.向外公布一个实现接口。——大家来实现哦,我用就好了,你办事儿我放心!
2.设置一组设置注入的方法。——具体用的时候你交给我就好了,我给你一套规范你交给我所有我需要的!

我们公布一组接口类型:
interface JustForA{
    public void doSomething();


于是,原来A中的doSomething被承包给接口了,A需要持有对该接口实现的子类引用:
class A{
    private JustForA doing;
}

当然,设成private不行,要和外界交互啊,加入注册/注销方法:
class A{
    private JustForA doing;
    public setInDo(JustForA doThings){
        doing = doThings;
    }
    public setOffDo(s){
        doing = null;
    }
}

之后可以设计A的doSomething功能了:

class A{
    private JustForA doing=null;
    public void setInDo(JustForA doThings){
        doing = doThings;
    }
    public void setOffDo(s){
        doing = null;
    }

    public void Doing(){
        if(doing!=null){
           doing.doSomething();
        }        
        else{
            //default initialize do or do nothing
        }
    }
}

于是,我们在主函数里头用到A的时候,需要用匿名内部类去造一个JustForA的子类对象

public static void main(String args[]){
    A a = new A();
    a.setInDo(new JustForA(){
        @override
        public void doSomething(){
            //具体代码。
        }
    }).Doing();
    

}

这种写法估计做安卓的小伙伴们会很熟悉吧,java按钮button组件最常用这种匿名内部类的实现了。实际上这就是一种控制反转的思想,因为本身按钮该做什么应该是按钮自身的内部函数吧~而实际上你在实现的时候,传入的是一个new好的Listener对象参数,而做事的,是Listener中被Override的方法。

通过这种方法,实现了控制各类Button的点击方法。

上简单的例子代码吧:


首先是被注入依赖的类的定义如下:

package IoCModel;

public class ControlBody {
	private  BodyListener listener=null;

	public void setListener(BodyListener listener) {
		this.listener = listener;
	}
	
	public void setOffListener(){
		listener=null;
	}
	
	public void Work(){
		if(listener!=null){
			listener.doSomething();
		}
		else{
			System.out.println("没有动态绑定的事件!");
		}
	}
	
}
看这个简单的片段,三点说明:

1.这个类持有真正做事的接口的一个对象Listener(具体实现对象)


2.这个类设置了如何设置做事的对象。


3.这个类真正做事的方法work实际上调用的是具体实现对象Listener的方法。

之后是定义对外接口的代码,这个很简单了,就一个抽象方法:

package IoCModel;

public interface BodyListener {
	void doSomething();
}
最后是测试代码:

public class MainTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ControlBody body = new ControlBody();
		body.setListener(new BodyListener() {
			
			@Override
			public void doSomething() {
				System.out.println("我在做第一件事啊!"+this.toString());
				
			}
		});
		body.Work();
		body.setOffListener();
		body.Work();		
		body.setListener(new BodyListener() {
			
			@Override
			public void doSomething() {
				System.out.println("我又在做另一件事情啊!"+this.toString());
				
			}
		});
		body.Work();
		
	}

}
实现很简单粗暴吧?就是匿名内部类生成覆盖doSomething方法的不同实例而已。

咱要写的高雅一点,不妨可以直接再声明单独的类或者内部类去做事,然后具体哟个的时候,只需要在body.setListener()函数里把参数传成具体类的实例即可。


显然,如果我有一个BodyListener接口的A类实现,那注入的以来就是A类依赖对象,相互作用的是A类和调用类ControlBody;

如果我又有一个BodyListener接口的B类实现,那注入的以来就是B类依赖对象,相互作用的是B类和调用类ControlBody,这时候你A类爱怎么改怎么改,和主体调用无关了,你自行编译吧,我主体代码无需更改。

这也就是java多态的魅力呢。




你可能感兴趣的:(JAVA菜鸟学习日记——简单代码实现IOC控制反转)