我是Guice的新手,我想知道我能走得多远。
我有一个接口UserInfo
,其中包含多个实现类GoogleUserInfo
,FacebookUserInfo
,TwitterUserInfo
等。这些类是使用工厂创建的
public class UserInfoFactory {
public UserInfo createFromJsonString(String jsonspec) {
.
.
.
}
}
创建由JSON字符串jsonspec
控制,该字符串控制返回UserInfo
的哪个实现类。具体来说,有一个JSON字符串元素domain
来控制创建。创建实际上是使用GSON对jsonspec
进行反序列化的函数
我想知道是否有一种很好的方法可以用Guice依赖注入替换这个创建?
答案 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
只是一个接口或一个抽象类(为清晰起见,在此省略)。