论坛首页 Java企业应用论坛

还是没有明白IoC的好处

浏览 58227 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-03-11  
引用

另外“什么client要传入组件”,你说的client是使用者,你不会不懂封装下,再给调用者使用啊

谁来封装? 你这只是转移了职责,把组件的复杂性转移到了client?如何测试client?你会遇到相同的问题,把构造问题上移了。说到地要ioc就必须有这么个结构:
className{
     //定义所有用到的想ioc的组件
    //所有方法都不能在方法内部构造ioc的组件
}
否则你就很难注射进去.我承认ioc有它的好处,但是他又它的局限性,如此严格的构造,会对你的设计有妨害的,尤其是封装性
0 请登录后投票
   发表时间:2005-03-11  
引用

使用接口隔离啊,客户段永远都不知道有这个方法,除非他够狠,向下cast

很简单 bo要用到dao,dao要用到connection
呢么connection 要注射到dao中,dao要注射到bo中,都是接口;
请问第一个instance 是谁负责来拿?
0 请登录后投票
   发表时间:2005-03-11  
//所有方法都不能在方法内部构造ioc的组件
这是类设计者考虑的问题"是方法内部变量或是类的属性"。
0 请登录后投票
   发表时间:2005-03-11  
在测试的时候,instance的创建当然是我的代码。
在实际使用的时候,我会假设有个全能的组装者,比如是spring啦,狠点,我让jdk帮我做这件事,但实在有点困难,希望jdk快点支持。
当然在实际的情况中,确是狠复杂,只在你认为需要IOC的地方IOC。
0 请登录后投票
   发表时间:2005-03-11  
引用

这是类设计者考虑的问题"是方法内部变量或是类的属性"。

方法内部的变量你怎么注射?
0 请登录后投票
   发表时间:2005-03-11  
引用

在实际使用的时候,我会假设有个全能的组装者,比如是spring啦,

在那里组装,就会在哪里产生依赖(比如spring),其实你只是把依赖延迟了,并且扩散到了客户的代码中
0 请登录后投票
   发表时间:2005-03-12  
再锻炼一下自己的解释问题的能力。

ioc是一种类的实现方法,而不是关于接口设计的方法。
它的关注点是我怎么实现这个类最简单灵活
而不关注别人如何调用我的模块最简单明了。


设计模块A的实现,当它需要从另外一个模块得到功能B的时候,基本有两种选择:

1。直接静态地找到实现B的模块。自己用new创建B的对象,或者通过一个静态工厂,service locator。都属于这个范畴。
这种情况其实很常见。比如,我内部需要一个查找表,自己直接new一个HashMap就好了,谁耐烦从外界ioc进来一个Map这么麻烦?
或者,大家平时都自己new一个StringBuffer吧?没人从外面ioc吧?

采用这个方法的前提是:
  a.  自己具体选取哪个模块对外界完全透明。比如,我用HashMap还是TreeMap,只影响效率,但是不影响我这个模块对调用者承诺的语义。
  b. 或者,实现这个功能的B模块已经是一个100%的标准。

这两个前提满足任何一个,都没必要也不应该ioc。


2。从外界动态接收一个实现功能B的实例。
这就是ioc了。不过,ioc本身并不规定注射进来的就必须是一个接口。注射一个具体类,抽象类也是ioc。只不过一般来说,根据接口编程原则,用接口更加好。

采用它的前提是:
  a. 不同的B的实现会影响A的语义。而在设计A的时候我们不能决定选择哪个实现。
  b. 或者,实现B的模块没有一个标准,考虑到移植性的问题,可以用ioc来把责任推诿到其它模块。


采用ioc的几个注意点:
  a. 最好是要ioc进来的组件是immutable的,没有状态。这样的ioc最简单。

  b. 如果需要状态变化,那么关于状态变化的语义一定要定义清楚。调用者和被调用者各自改变组件的状态都有什么结果要明确。

  c. 如果模块内部需要自己控制对象的生命期,此时就要ioc进来一个工厂。(注意,工厂也是一个普通的java对象,自然也可以ioc。同理,工厂的工厂,工厂的工厂的工厂,都可以ioc。不要一看见工厂字样就觉得和ioc抵触。工厂不过是一个有着一个create()函数的对象而已)

  d. 不要杞人忧天,想着别人如何把这个对象给你。OO是一个职责分配的体系。你该关心的是如何用这个组件实现你自己的功能,至于组件怎样来,不是你的责任,你不要管也不应该管。

  e. 要学会偷懒和推卸责任。通过把寻找对象的职责推诿出去,你可以保证少挨骂。如果组装者组装了一个错误的组件,那么责任在他,你大可以跳起脚来骂。而如果你自己越俎代庖弄错了,责任则全部在你。

  f.  即使组装者也是你自己,ioc至少保证了改正错误或者扩展功能的代价最小化。你需要修改的只是组装部分,而不是包含者业务逻辑的业务对象。



好了。说完怎么用ioc实现模块。再来说怎么使用一个ioc设计的模块。

正如frankensteinlin所说,调用者直接调用一个ioc模块并不是最简单最直观的。
new AImpl(new BImpl()).f()
明显比new DefaultAImpl().f()要麻烦。

但是,很多时候调用者自己也不必惹这个麻烦的。我的模块C如果需要A的功能,大可以继续推诿责任,让别人给我一个A的实例就是。

如此,创建C的时候就变成:
new CImpl(new AImpl(new BImpl(););););;



这样层层推诿,最终会有一个组装者,他把所有的对象组装责任接过来,他的唯一责任就是组装各个组件,而没有业务逻辑方面的职责。


不过,我要反对age0以及很多其它人的是:

ioc并不是说系统只能有一个终极对象组装者(比如配置文件什么的)。

系统是模块套模块的。有时候模块A不能决定,推诿出去,模块B不能决定,也推诿出去,但是模块C可能发现此时选择AImpl1, 还是AImpl2, 选择BImpl1还是BImpl2对它要实现的语义来说已经符合上面说的不用ioc的前提了,那么此时,对象C就可以作为局部的A和B的组装者,没必要把这个职责再推诿出去了。

而这些组装策略也不是一言堂。根据不同的组装者的需要,可以选择直接new,静态工厂,service locator,或者配置文件。

这里面没有东风压倒西风的问题,完全是根据具体需要选择合适的组装方法。



最后,重提java.io。


当sun设计BufferedReader的时候,虽然从它的功能上你可以叫它decorator,但是它的设计方法却无疑是ioc的。
它从外界接受一个Reader实例,然后对之进行缓存处理。
它不对输入的Reader到底是哪个Reader做任何假设或者要求。它只是简单地在构造函数里宣称需要一个Reader。


然后,当我们的一个模块X使用BufferedReader的时候,可能我们已经知道需要给它一个什么Reader,此时,就没必要继续ioc了,在这个X模块里,ioc到此为止,我们直接组装对象就好了。

有没有可能一个Reader要在终极的配置文件里由deploymentor决定采用哪个实现呢?理论上应该也有可能,只不过,我至今还没有遇到过。
0 请登录后投票
   发表时间:2005-03-12  
越搅越糊涂了,我写了四个例子,劳烦ajoo及各位指点一下,哪个是ioc,哪个不是ioc。

interface inner;

class inner1 implements inner;
class inner2 implements inner;


例1:
class packet
{
	inner m_inner;
	
	packet(int type);
	{
		switch(type);
		{
			case 2:	m_inner = new inner2();;	break;
			
			case 1:
			default:	m_inner = new inner1();;	break;
		}
	}
}


例2:
class packet
{
	inner m_inner;
	
	packet(int type);
	{
		m_inner = InnerFactory.GetInner(type);;
	}
}


例3:
class packet
{
	inner m_inner;
	
	packet(inner in);
	{
		m_inner = in;
	}
}

PacketFactory
{
	packet GetPacket(type);
	{
		switch(type);
		{
			case 2:	return new Packet(new Inner2(););;
			
			case 1:
			default:	return new Packet(new Inner1(););;
		}
	}
}


例4:
class packet
{
	inner m_inner;
	
	packet(inner in);
	{
		m_inner = in;
	}
}

PacketFactory
{
	packet GetPacket(type);
	{
	    DefaultPicoContainer container = new DefaultPicoContainer();;

		switch(type);
		{
			case 2:
				container.registerComponentImplementation(inner2.class);;
				break;
				
			case 1:
			default:
				container.registerComponentImplementation(inner1.class);;			
				break;
		}
		container.registerComponentImplementation(packet.class);;
	    packet p = (packet); container.getComponentInstance(packet.class);;
	
		return p;
	}
} 
0 请登录后投票
   发表时间:2005-03-12  
IOC又叫依赖载入,继续讨论什么是IOC就没意思啦。

依赖载入和ServiceLocator两种模式都是用于把对象与其依赖的对象解藕。
首先设计的时候,要确认你需要这种解藕,那好,我们可以从两种方式选一。

为什么ajoo提到的io库不使用ServiceLocator呢,为什么spring使用依赖载入呢 ?
两个的共同特征都是以框架命名的,框架的特点就是把各种外部组件搭配起来,外部组件的创建框架跟本就不想关心,也关心不了。
而在应用中经常遇到的XXService->xxDAO这种,简直就是一百年不换的,那ServiceLocator就够了,也不必要依赖载入。
0 请登录后投票
   发表时间:2005-03-15  
age0,
你的前两个例子自然不是ioc,后两个是。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics