锁定老帖子 主题:还是没有明白IoC的好处
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2005-03-11
引用 另外“什么client要传入组件”,你说的client是使用者,你不会不懂封装下,再给调用者使用啊 谁来封装? 你这只是转移了职责,把组件的复杂性转移到了client?如何测试client?你会遇到相同的问题,把构造问题上移了。说到地要ioc就必须有这么个结构: className{ //定义所有用到的想ioc的组件 //所有方法都不能在方法内部构造ioc的组件 } 否则你就很难注射进去.我承认ioc有它的好处,但是他又它的局限性,如此严格的构造,会对你的设计有妨害的,尤其是封装性 |
|
返回顶楼 | |
发表时间:2005-03-11
引用 使用接口隔离啊,客户段永远都不知道有这个方法,除非他够狠,向下cast 很简单 bo要用到dao,dao要用到connection 呢么connection 要注射到dao中,dao要注射到bo中,都是接口; 请问第一个instance 是谁负责来拿? |
|
返回顶楼 | |
发表时间:2005-03-11
//所有方法都不能在方法内部构造ioc的组件
这是类设计者考虑的问题"是方法内部变量或是类的属性"。 |
|
返回顶楼 | |
发表时间:2005-03-11
在测试的时候,instance的创建当然是我的代码。
在实际使用的时候,我会假设有个全能的组装者,比如是spring啦,狠点,我让jdk帮我做这件事,但实在有点困难,希望jdk快点支持。 当然在实际的情况中,确是狠复杂,只在你认为需要IOC的地方IOC。 |
|
返回顶楼 | |
发表时间:2005-03-11
引用 这是类设计者考虑的问题"是方法内部变量或是类的属性"。 方法内部的变量你怎么注射? |
|
返回顶楼 | |
发表时间:2005-03-11
引用 在实际使用的时候,我会假设有个全能的组装者,比如是spring啦, 在那里组装,就会在哪里产生依赖(比如spring),其实你只是把依赖延迟了,并且扩散到了客户的代码中 |
|
返回顶楼 | |
发表时间: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决定采用哪个实现呢?理论上应该也有可能,只不过,我至今还没有遇到过。 |
|
返回顶楼 | |
发表时间: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; } } |
|
返回顶楼 | |
发表时间:2005-03-12
IOC又叫依赖载入,继续讨论什么是IOC就没意思啦。
依赖载入和ServiceLocator两种模式都是用于把对象与其依赖的对象解藕。 首先设计的时候,要确认你需要这种解藕,那好,我们可以从两种方式选一。 为什么ajoo提到的io库不使用ServiceLocator呢,为什么spring使用依赖载入呢 ? 两个的共同特征都是以框架命名的,框架的特点就是把各种外部组件搭配起来,外部组件的创建框架跟本就不想关心,也关心不了。 而在应用中经常遇到的XXService->xxDAO这种,简直就是一百年不换的,那ServiceLocator就够了,也不必要依赖载入。 |
|
返回顶楼 | |
发表时间:2005-03-15
age0,
你的前两个例子自然不是ioc,后两个是。 |
|
返回顶楼 | |