`
kyo100900
  • 浏览: 639711 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【Danny hui】运用抽象工厂模式自己动手写一个IoC

阅读更多

本文的作者Danny hui似乎是TTS上的新人,我从Google搜不出一点关于本人的信息。从通过本文可以看出他对模式与IoC有自己独到的见解,本文在TTS上引发很多网友回帖,反响不一。那么我们现在来看看作者的IoC之路吧。

 

 原文:http://www.theserverside.com/tt/articles/article.tss?l=InjectionwithAbstractFactory

 

简介

 

本文重点讨论的是DI(依赖注入)结合设计模式中的Abstract Factory(抽象工厂模式)的优势与弊端。该方式尤其适合以下场合:

l         通过dynamic parameters(动态参数)来建立一个local stateful对象

l         当创建对象时抛出了checked exception时进行相应处理,

l         动态的封装对象

Spring IoC容器,PicoContainer以及Guice都无法圆满的解决这些问题或者说它们几乎做不到!!!

 

Abstractory Factory模式来实现DI

 

 

 

现在通过下面两种途径来改进经典GoF中的Abstract Factory

l         一个工厂接口来代替抽象工厂类(可行)

l         每一个工厂方法的职责就是创建对象,并为其注入依赖

 

 

来看一个简单的例子吧:

 

 

在这里,ComponentA依赖于ComponentB。为方便进行单元测试ComponentAImpl类,接口ComponentB的实现必须注入到ComponentAImpl中去。下面看看采用Abstract Factory模式完成依赖注入的Java实现代码:

 

 //Abstract Factory for Dependency Injection
//Factory interface
public interface Module1ServiceFactory {
 ComponentA getComponentA();
 ComponentB getComponentB();
}

//Concrete factory
public class Module1ServiceFactoryImpl implements Module1ServiceFactory {
 private ComponentA componentA;
 private ComponentB componentB;
 private Module1Servicefactory instance;

 private Module1ServicefactoryImpl() {}

 public static synchronized Module1ServiceFactory getInstance() {
  if (null == instance) {
   instance = new Module1ServicefactoryImpl();
   componentA = new ComponentAImpl();
   componentB = new ComponentBImpl();
   componentA.setComponentB(componentB);
  }
  return instance;
 }

 public ComponentA getComponentA() {
  return componentA;
 }

 public ComponentB getComponentB() {
  return componentB;
 }
}

//Client
public class Client {

 public static void main(String[] args) {
  Module1ServiceFactory m1sf =
   Module1ServicefactoryImpl.getInstance();
  ComponentA componentA = m1sf.getComponentA();
  componentA.operationA1();
 }
}

 

 

 

 

 

延迟加载

 

延迟加载对象可以通过改变方法来实现,比如说我现在要稍微对getComponentA()进行改动,请看:

 

 public synchronized ComponentA getComponentA() {
  if (null == componentA) {
   componentA = new ComponentAImpl();
  }
  return componentA;
 }

 

当然我们的Module1ServiceFactoryImpl.getInstance()方法也要进行相应的改动了,我们可以通过传递一个参数来判断Module1ServiceFactoryImpl.getInstance()是否需要创建对象。

 

 

 

Singleton作用域

 

上面的代码仅仅只是建立singleton对象。如果需要在每次调用getComponentA()getComponentB()的时,都返回新创建的对象的话,我们可以对我们的Abstract Factory进行下面的改动:

 

//Concrete factory
public class Module1ServiceFactoryImpl {
 private Module1ServiceFactory instance;

 private Module1ServiceFactoryImpl() {}

 public static synchronized Module1ServiceFactory getInstance() {
  if (null == instance) {
   instance = new Module1ServiceFactoryImpl();
  }

return instance;
 }

 public ComponentA getComponentA() {
  ComponentA componentA = new ComponentAImpl();
  ComponentB componentB = getComponentB();
  componentA.setComponentB(componentB);
  return componentA;
 }


 public ComponentB getComponentB() {
  return new ComponentBImpl();
 }
}

 

 

 

 

 

类似的,我们还可以将一个singleton对象注入到非singleton对象中去。比如说,我们假设ComponentB此时是singletonComponentA为非singleton,那么我们可以这样:

 

//Concrete factory
public class Module1ServiceFactoryImpl {
 private Module1ServiceFactory instance;
 private ComponentB componentB;

 private Module1ServicefactoryImpl() {}

 public static synchronized Module1ServiceFactory getInstance() {
  if (null == instance) {
   instance = new Module1ServiceFactoryImpl();
   componentB = new ComponentBImpl();
  }

return instance;
 }

 public ComponentA getComponentA() {
  ComponentA componentA = new ComponentAImpl();
  componentA.setComponentB(componentB);
  return componentA;
 }

 public ComponentB getComponentB() {
  return componentB;
 }
}

 

将一个非singleton对象注入到singleton对象也不是做不到,但这种应用场合在现实世界中是非常罕见的。尽管如此,但在使用dynamic parameters赋级一个locallocal变量时,创建一个非singleton对象却很普遍。接下的话题,我们就谈谈这个。

 

 

 

dynamic parameterssingleton创建一个local stateful对象

 

这是所有IoC框架所面临的问题。下面的代码中,仍然假定ComponentAsingletion

 

 

  public void operationA2() {
  String s = aPrivateMethod();
  int i = anotherMethod();
  ComponentC componentC = new ComponentCImpl(s, i);
  //do something else.
 }

 

 

这里,ComponentAImpl用到了ComponentC接口。虽然,为了更IoC,我们需要将它注入ComponentC的实现,而不是直接硬编码在ComponentAImpl.operationA2()方法中去,这样做还有一个好处就是,方便单元测试。但问题来了,我们不能将ComponentC作为一个实例变量,因为它是有状态的(stateful),它维持着某一个特定的客户端状态,不能与其它客户端进行共享。因此,不能使用常用的setterconstrutctor注入方法来实现。

 

使用Abstract Factory模式的话,有两个方法可以解决这个问题。不过,都得改动Module1ServiceFactory接口,添加下面方法:

 

  ComponentC getComponentC(String s, int i);

 

 

请看我在Module1ServiceFactoryImpl中的实现代码:

 

public ComponentC getComponentC(String s, int i) {
  return new ComponentCImpl(s, i);
 }
  

 

第一种方法就是将包含它的“工厂”注入到所需的local stateful对象中去:

 

 private Module1ServiceFactory factory;
 public void setModule1ServiceFactory(Module1ServiceFactory factory) {
  this.factory = factory;
 }

//ComponentAImpl.operationA2() becomes:

 public void operationA2() {
  String s = aPrivateMethod();
  int i = anotherMethod();
  ComponentC componentC = factory.getComponentC(s, i);
  //do something else.
 }

 

 

 

 

缺点显而易见:ComponentAImpl现在与Module1ServiceFactory绑定在一起了,如果要对ComponentAImpl进行单元测试的话,我们不得不mock一个Module1ServiceFactory实现。纵然有这个缺陷,但直接为stateful对象注入“工厂”对象的方法也最为简单。类似的技术广泛的在J2EE领域采用,比如说JPA,将我们将entity manager factory可以注入到应用程序代码后,它就专门负责管理自身创建的application-managed entity(注:如果熟悉Hibernate的话,也可以将entity manager factory想象成HibernateSessionFacory application-managed entity想象成Session

 

 

第二种方法就是将方法抽出,移到抽象类中去,便于单元测试:

 

 

 

 public abstract class AbstractComponentA implements ComponentA {
public void operationA2() {
  String s = aPrivateMethod();
  int i = anotherMethod();
  ComponentC componentC = getComponentC(s, i);
  //do something else.
 }

 public abstract ComponentC getComponentC(String s, int i) ;

}

public class ComponentAImpl extends AbstractComponentA {
 public ComponentC getComponentC(String s, int i) {
  return new ComponentCimpl(s, i);
 }

}

 

 

这种方式类似于Springframework的方法注入(Metod Injection),不过Spring不需要传递dynamic parameter也能创建stateful对象。此时,单元测试可以不需要再实现任何mock工厂。但是,这仍然是一个笨拙的办法。设想一下,我们的类里如果有10个这样的local stateful对象,那么我们需要提供10个抽象方法,才能再次让单元测试变得简单,可是代价就造成是更加混乱的应用程序代码。

 

Springframework还可以通过使用Java反射机构还解决类似问题。但这更加复杂了,并且不适合正常应用程序编码工作。

 

 

处理创建对象时抛出的checked exceptions

 

这个问题也是让IoC容器头痛的。如果checked exception在对象创建时抛出,应用程序可能希望是能捕获并且能够恢复。我们来看一下这个关于Web Service的需求实例:当客户端尝试建立web servicestub时,并且此时服务端web service还不可用,那么客户端是能够捕获stub所抛出的异常,然后显示相应信息,并询问用户是否稍后继续再次连接。如果单纯用IoC容器的话,抛出具体指定的checked exception是很困难的。而手工代码却可以很轻松的解决这个问题——我们可以简单的将“工厂”的checked exceptions抛出,留给应用程序代码去手工处理或者恢复它们。

 

 

动态封装对象

很多场合下,一个接口对应着多个不同的实现类,类型实例就是设计模式中的策略模式。那么,用一个参数就可以决定具体哪个实现类应该被注入到相应的封装对象中去。然而使用基于XMLIoC容器是静态封装对象,很难实现此功能。也许编程式的IoC容器可以解决动态依赖问题,但我要说的是我们的Abstract Factory则更简单直接,看看下面的代码就知道了:

 

 

 

 

 //Concrete factory
public class Module1ServiceFactoryImpl {
 ...

 public ComponentA getComponentA(int strategy) {
  ComponentA componentA = new ComponentAImpl();
  ComponentB componentB = getComponentB(strategy);
  componentA.setComponentB(componentB);
  return componentA;
 }


 public ComponentB getComponentB(int strategy) {
  switch(strategy) {
   case STRATEGYA:
    return new StrategyA();
   case STRATEGYB:
    return new StrategyB();
   default:
    return null;
                        }
 }
}

 

 

 

注意这里StrategyAStrategyB共享实现了ComponentB接口。

 

 

 

 

结束语

 

今天我们谈到的运用依赖注入和Absratct Factory设计模式来解决下列问题:

1.   通过动态参数,创建local stateful对象

2.   处理创建对象时抛出的checked exceptions

3.   动态封装注入对象

 

除此以外,该方法与其它IoC容器相比,性能更好,毕竟是直接硬编码嘛。那么最大的缺点自然就是要手工写很多基础代码了,并且如果要延迟加载与主动加载之间来回切换的话,代码的改动量是很可观的。不过呢,这样的需求几乎是不存在的。

 

可单元测试的关键点是基于接口而非实体类编程。这样的话mock对象可以注入到任何需要注入的地方去。

 

不管怎么样,有时候在我们的应用程序中,依赖注入是一个不得不解决的问题。所有IoC容器以及手工的依赖注入解决方案都是专注于各自的领域——Spring IoC使用XML配置,Google Guice使用特殊的Java,我们的Abstract Factory也是如此。通过这些解决方案,我们可以避免应用程序中到处显现依赖的编码,类与类之间耦合度降低,使用Mock对象就可以正常进行单元测试。

对所有的IoC容器来说,无论是声明式的还是编程式的,它们的目的就是作为一个对象创建和封装工厂。同样我们的Abstract Factory也是如此,只不过这是一个可定制的依赖注入解决方案。

最后,使用IoC容器,我们可以消除类与类之间的依赖,从而让单元测试变得更加简单。但代价就是你不得不再次依赖于第三方Jar包或XML配置文件。IoC容器并没有一个统一的标准,每个框架所提供的特性和功能都是不同的,因为一旦你使用了某个IoC框架,就意味

10
2
分享到:
评论
3 楼 梦幻酷酷 2008-04-29  
非常不错.... 
2 楼 xuejianshan 2008-04-26  
几个小时就搞定拉,高手~~~
1 楼 kyo100900 2008-04-23  
 终于弄好了,呵呵,花了几个小时。
EJB3.1第三部分刚刚出来了,下次给大家介绍那个吧。

相关推荐

    IoC容器的设计(利用反射、注解和工厂模式实现)

    1. 利用注解、反射和工厂模式设计一个简单的IoC容器 2. 该IoC容器包含3个注解和一个IoC容器类(AnnotationConfigApplicationContext),其定义如下: 注解 含义 @Component 标注Bean @Autowired 标注需要被注入的...

    工厂模式的测试并有spring ioc的简单实现

    1. **简单工厂模式**(simpleFactory):这种模式中,我们通常定义一个工厂类,它根据传入的参数来决定创建哪种具体的产品对象。简单工厂模式适用于产品种类不多,且不会频繁扩展的情况。在测试中,我们可以模拟不同...

    工厂模式的IoC应用

    工厂模式的IoC应用,特别是在Spring框架中,是一个强大的工具,可以显著提高代码的可维护性和可测试性。`BeanFactory`作为Spring的核心组件,负责bean的创建、依赖注入、生命周期管理和作用域控制,使得开发人员能够...

    抽象工厂 asp.net 内含详细代码示例

    以下是一个简单的抽象工厂模式在ASP.NET中的应用示例: ```csharp // 定义抽象工厂接口 public interface IPageFactory { Page CreateWebFormPage(); View CreateMvcView(); } // 具体工厂实现,针对ASP.NET Web...

    手写一个SpringIoc容器

    本项目"手写一个SpringIoc容器"旨在模仿Spring的IOC(Inversion of Control,控制反转)功能,帮助开发者深入理解Spring的工作原理,提升对依赖注入(Dependency Injection)模式的认识。 在实现自定义的Spring IOC...

    ioc模式与工厂模式的比较[借鉴].pdf

    工厂模式通过一个公共的工厂类来创建不同类型的产品,可以根据需求动态地切换产品实现。例如,客户端代码只依赖于工厂接口,而不知道实际创建的对象类型。工厂模式的实现方式多样,包括简单工厂、抽象工厂和工厂方法...

    IOC模式 c#经典例子

    在这个“IOC模式 c#经典例子”中,我们可以看到如何使用Unity,一个流行的.NET框架,来实现依赖注入。 Unity是Microsoft提供的一个轻量级的DI容器,它可以帮助开发者管理对象的生命周期,并在运行时将依赖关系注入...

    自己动手做框架——ORM,MVC,IOC框架

    在IT行业中,框架是软件开发中的重要组成部分,它们提供了一种结构化的方式来组织代码,使得开发者可以更加高效...动手做框架是一个挑战,但这个过程将极大地丰富你的技术视野,帮助你在未来项目中更好地运用这些技术。

    设计模式-工厂模式

    抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。这种模式适用于当需要创建的对象是一系列相互依赖的对象族,且这些对象的类都处于不同的抽象层次时。在Java中,抽象工厂通常...

    DAO抽象工厂

    DAO(Data Access Object)抽象工厂是一种设计模式,它在软件工程中用于创建DAO对象的类工厂。DAO模式的主要目的是为了封装对数据源的访问,使得业务逻辑层与数据存储层解耦,提高代码的可测试性和可维护性。在Java...

    自己动手实现IOC和MVC源码

    【标题】"自己动手实现IOC和MVC源码"揭示了本文的核心内容,即通过实践来理解并构建IoC(Inversion of Control)和MVC(Model-View-Controller)这两种重要的软件设计模式的源代码。这两者是现代Java Web开发中...

    spring ioc模块手写demo

    spring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demo...

    springIOC手写框架分析

    springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC...

    Java Spring代理模式AOP-IOC分析

    Java Spring代理模式AOP-IOC分析 一、代理模式概述 在软件设计中,代理模式是一种常用的设计模式。...本文对Java Spring代理模式AOP-IOC进行了分析和讨论,并提供了一个示例项目来演示如何使用Spring实现AOP和IOC。

    手写SpringIoc的XML实现方式

    在本主题中,我们将深入探讨如何手写一个基于XML的Spring IOC实现,模仿Spring框架中的`ClassPathXmlApplicationContext`。这个实现旨在帮助理解Spring底层的工作原理,并加深对IoC容器的理解。 首先,我们需要创建...

    IoC容器和DependencyInjection模式

    在它们的背后有着同一个模式,这个模式决定了这些容器进行组 件装配的方式。人们用一个大而化之的名字来称呼这个模式:“控制反转”( Inversion of Control,IoC)。在本文中,我将深入探索这个模式的工作原理,给...

    手写SpringIOC注解实现版本

    这个"手写Spring IOC注解实现版本"项目旨在帮助我们深入理解Spring框架的工作原理,通过实际动手实现来增强对IoC容器和注解驱动编程的理解。 在Spring中,IOC意味着应用程序不再直接创建对象,而是将对象的创建和...

    手动实现一个ioc容器.zip

    标题 "手动实现一个IOC容器.zip" 提到的主题是关于如何从零开始构建自己的依赖注入(Dependency Injection,简称DI)容器,这在Java开发中尤为重要,特别是对于Spring框架的理解和学习。IOC容器是Spring的核心功能之...

    IoC小例子(了解一下IoC设计模式入门)

    IoC设计模式的实现通常依赖于容器,如Spring框架在Java中就是一个著名的IoC容器。开发者可以通过XML配置、注解或者编程式的方式来定义对象及其依赖关系。例如,在Spring中,我们可以在XML配置文件中定义Bean(代表一...

    用java写的一个简单的ioc实现,初学的朋友可以看看

    通过自己动手实现,初学者可以更好地理解Spring是如何在幕后管理对象和处理依赖的,这对于提升编程技能和设计模式的理解非常有益。同时,这也是软件开发中常用的一种学习方法——通过模仿和实践来深入理解复杂的概念...

Global site tag (gtag.js) - Google Analytics