4

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

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

  }
}

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

4

3 回答 3

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>如果您只需要接口并且希望在将来的某个时间更改具体类,这也允许您注入 a 。如果你想TwitterUserInfo接受构造函数参数,辅助注入 帮助你创建一个TwitterUserInfoFactory虽然,这将有助于它实现不变性

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可能没有任何依赖关系——对我来说这听起来像是一个数据对象——所以让你的类完全按照你找到它的方式(使用new)可能是最好的方法。虽然解耦接口和类以简化测试是件好事,但维护和理解是有成本的。Guice 是一把非常强大的锤子,但并非所有东西实际上都是要敲的钉子。

于 2013-04-15T23:32:21.803 回答
1

您可能需要使用辅助注射。

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

然后你需要配置你的工厂类

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

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

认为。我对自己很陌生。

于 2013-04-14T18:12:51.070 回答
1

这是我对 Guice 的抽象工厂模式的变体——使用多重绑定来绑定具体工厂。

在这里,我使用一组工厂和一个循环来选择合适的工厂类,但如果可能的话,您也可以使用 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只是一个接口或一个抽象类(为清楚起见,此处省略)。

于 2019-05-07T13:35:27.377 回答