用Guice取代抽象工厂?

时间:2013-04-14 18:06:02

标签: guice factory-pattern

我是Guice的新手,我想知道我能走得多远。 我有一个接口UserInfo,其中包含多个实现类GoogleUserInfoFacebookUserInfoTwitterUserInfo等。这些类是使用工厂创建的

public class UserInfoFactory {
  public UserInfo createFromJsonString(String jsonspec) {
    .
    .
    .

  }
}

创建由JSON字符串jsonspec控制,该字符串控制返回UserInfo的哪个实现类。具体来说,有一个JSON字符串元素domain来控制创建。创建实际上是使用GSON对jsonspec进行反序列化的函数 我想知道是否有一种很好的方法可以用Guice依赖注入替换这个创建?

3 个答案:

答案 0 :(得分:7)

您可以将Guice集成到工厂中,但您的代码可能与此情况完全相同。

这实际上是那些不容易替换的工厂之一,因为它必须包含解析jsonSpec的逻辑并根据它返回它返回的具体类型。让我们说工厂的略微简化的版本看起来像这样:

public class UserInfoFactory {
  public UserInfo createFromJsonString(String jsonspec) {
    if(getUserType(jsonSpec) == TWITTER) {
      return new TwitterUserInfo(jsonSpec);
    } else { /* ... */ }
  }

  private UserInfoType getUserType(String jsonSpec) { /* ... */ }
}

这种逻辑必须存在于某个地方,而你自己的UserInfoFactory似乎是一个完美的家。但是,因为您使用new,您将无法注入任何TwitterUserInfo的依赖项或其依赖项的依赖项 - 而 是问题的类型Guice很好地解决了。

可以TwitterUserInfo作为Provider注入,这样您就可以访问尽可能多的完全注入TwitterUserInfo个对象:

public class UserInfoFactory {
  @Inject Provider<TwitterUserInfo> twitterUserInfoProvider;

  public UserInfo createFromJsonString(String jsonspec) {
    if(getUserType(jsonSpec) == TWITTER) {
      TwitterUserInfo tui = twitterUserInfoProvider.get();
      tui.initFromJson(jsonSpec);
      return tui;
    } else { /* ... */ }
  }
}

...当然,如果您只需要界面并且想要在将来的某个时间改变具体类,那么这也允许您注入@Twitter Provider<UserInfo>。如果您希望TwitterUserInfo接受构造函数参数,Assisted injection 帮助您创建TwitterUserInfoFactory,这将有助于immutability

public class UserInfoFactory {
  @Inject TwitterUserInfo.Factory twitterUserInfoFactory;

  public UserInfo createFromJsonString(String jsonspec) {
    if(getUserType(jsonSpec) == TWITTER) {
      return twitterUserInfoFactory.create(jsonSpec);
    } else { /* ... */ }
  }
}

// binder.install(new FactoryModuleBuilder().build(TwitterUserInfoFactory.class));
public class TwitterUserInfo implements UserInfo {
  public interface Factory {
    TwitterUserInfo create(String jsonSpec);
  }

  public TwitterUserInfo(@Assisted String jsonSpec, OtherDependency dep) { /* ... */ }
}

最后一点注意事项: TwitterUserInfo可能没有任何依赖关系 - 这对我来说听起来像是一个数据对象 - 所以请让您的课程完全按照您的方式找到它(使用{ {1}})可能是最好的方法。虽然很容易将解耦的接口和类放在一起以便于测试,但维护和理解会有成本。 Guice是一把非常强大的锤子,但并不是所有的东西都是钉锤。

答案 1 :(得分:1)

你可能想要使用辅助注射。

您需要使用(@Assisted String jsonspec)(当然还有@Inject)在GoogleUserInfo,FacebookUserInfo和TwitterUserInfo中注释您的构造函数

然后您需要配置工厂类

binder.install(new FactoryModuleBuilder().build(UserInfoFactory.class));

然后适当地绑定您想要使用的任何信息提供者。

认为。我很擅长自己。

答案 2 :(得分:0)

这是我与Guice一起制作的abstract factory pattern的变体-使用multibindings来绑定具体工厂。

在这里,我使用一组工厂和一个循环来选择合适的工厂类,尽管如果可能的话,您也可以使用Map并通过某个键来获取具体工厂。

public class AbstractFactory {
  @Inject Set<UserInfoFactory> factories;

  public UserInfoFactory createFor(String jsonSpec) {
    for (UserInfoFactory factory : factories) {
      if (factory.handles(jsonSpec))
        return factory;
    }
    throw new IllegalArgumentException("No factory for a given spec!");
  }
}

这项技术不需要您打开和修改AbstractFactory类来添加新的具体工厂-只需编写新工厂并将适当的绑定添加到Guice模块即可。

每个具体工厂负责处理特定的数据格式并正确解析json字符串:

public class FacebookUserInfoFactory implements UserInfoFactory {
  public boolean handles(String jsonSpec) {
    return jsonSpec.contains("facebook");
  }

  public UserInfo createFromJsonString(String jsonspec) {
    return new FacebookUserInfo(jsonspec);
  }
}

public class TwitterUserInfoFactory implements UserInfoFactory {
  public boolean handles(String jsonSpec) {
    return jsonSpec.contains("twitter");
  }

  public UserInfo createFromJsonString(String jsonspec) {
    return new TwitterUserInfo(jsonspec);
  }
}

现在,将您的混凝土工厂与Multibinder绑定。用法示例:

AbstractFactory abstractFactory = Guice
        .createInjector(new AbstractModule() {
          protected void configure() {
            Multibinder<UserInfoFactory> factoryBindings = Multibinder
                    .newSetBinder(binder(), UserInfoFactory.class);
            factoryBindings.addBinding().to(FacebookUserInfoFactory.class);
            factoryBindings.addBinding().to(TwitterUserInfoFactory.class);
          }
        }).getInstance(AbstractFactory.class);

UserInfoFactory factory1 = abstractFactory.createFor("twitter");
UserInfoFactory factory2 = abstractFactory.createFor("facebook");
UserInfo user1 = factory1.createFromJsonString("twitter user json string");
UserInfo user2 = factory2.createFromJsonString("facebook user json string");
System.out.println(user1.toString());
System.out.println(user2.toString());
}

输出为:

TwitterUserInfo{twitter user json string}
FacebookUserInfo{facebook user json string}

UserInfo只是一个接口或一个抽象类(为清晰起见,在此省略)。