Pulling dependencies out of "god" objects in a DI framework
I have a Servlet that has a lot of existing code. I'm trying to add dependency injection into one part of it. Currently I am doing it manually:
public class AdjustBookPriceHandler extends BookRequestHandler {
@Override
public void handleRequest(RequestState requestState, RequestData requestData, Object obj) {
Book book = (Book) obj;
long newPrice = Long.parseLong(requestData.getQueryParam("price");
OfferRepository offerRepository = ((BookData) requestState.getData()).getOfferRepository();
BookPriceAdjuster priceAdjuster = getBookPriceAdjuster();
priceAdjuster.adjustPrice(newPrice);
}
protected BookPriceAdjuster getBookPriceAdjuster(RequestState requestState, RequestData requestData, Book book) {
return new BookPriceAdjuster(book, offerRepository);
}
}
Here the book and offer repository dependencies are injected into the BookPriceAdjuster through the constructor. The getBookPriceAdjuster method is there to allow classes that inherit from the AdjustBookPriceHandler to provide a different price adjuster.
I would like to start using a DI framework like Guice to reduce some of the boilerplate code that complex examples would introduce. However, I'm unsure of the best开发者_如何学Python way to use it in this context.
How can I write bindings that would pull out the relevant dependencies from the "god" objects RequestState and RequestData? Or at this point would using a framework be just as complicated and messy?
I'm not sure if I understand correctly what you are trying to do. The book is request scoped and offerRepository is singleton scoped? If so you can do something like this..
public class AdjustBookPriceHandler extends BookRequestHandler {
// inject concrete instance with setter or constructor
private BookPriceAdjusterProvider bookPriceAdjusterProvider;
@Override
public void handleRequest(RequestState requestState, RequestData requestData, Object obj) {
Book book = (Book) obj;
long newPrice = Long.parseLong(requestData.getQueryParam("price");
BookPriceAdjuster priceAdjuster = bookPriceAdjusterProvider.getBookPriceAdjuster(book);
priceAdjuster.adjustPrice(newPrice);
}
}
public interface BookPriceAdjusterProvider {
BookPriceAdjuster getBookPriceAdjuster(Book book);
}
public class MyBookPriceAdjusterProvider {
// inject this through setter or constructor
private OfferRepository offerRepository;
protected BookPriceAdjuster getBookPriceAdjuster(Book book) {
return new BookPriceAdjuster(book, offerRepository);
}
}
This way, you get rid of the singleton scoped OfferRepository and can use some DI framework to do the injection for you. You can as well use different implementation of BookPriceAdjusterProvider if needed in your subclasses or so. Is this kind of what you wanted or have I misunderstood your objective completely?
How can I write bindings that would pull out the relevant dependencies from the "god" objects RequestState and RequestData?
You could start using Guice for dependency injection fairly easily here, depending on how your application is bootstrapped. Let's say you have a BooksModule.
public class BooksModule extends AbstractModule {
protected void configure() {
// Do Nothing
}
@Provides @RequestScoped
BookData provideBookData(RequestState requestState) {
return (BookData) requestState.getData();
}
@Provides @RequestScoped
OfferRepository provideOfferRepository(BookData bookData) {
return bookData.getOfferRepository();
}
}
Now you can use Guice to inject the dependencies into your BookPriceAdjuster class.
@RequestScoped
public class BookPriceAdjuster {
private final Book book;
private final OfferRepository offerRepository;
@Injected
public BookPriceAdjuster(Book book, OfferRepository offerRepository) {
this.book = book;
this.offerRepository = offerRepository;
}
// whatever methods it has
}
And now you can use a provider for your BookPriceAdjuster inside your servlet.
public class AdjustBookPriceHandler extends BookRequestHandler {
private final Provider<BookPriceAdjuster> bookPriceAdjusterProvider;
@Injected
public AdjustBookPriceHandler(Provider<BookPriceAdjuster> bookPriceAdjusterProvider) {
this.bookPriceAdjusterProvider = bookPriceAdjusterProvider;
}
@Override
public void handleRequest(RequestState requestState, RequestData requestData, Object obj) {
Book book = (Book) obj;
long newPrice = Long.parseLong(requestData.getQueryParam("price");
BookPriceAdjuster priceAdjuster = bookPriceAdjusterProvider.get();
priceAdjuster.adjustPrice(newPrice);
}
}
To bootstrap the application, you'll need install the BooksModule using the Guice injector. How you go about this depends on how your application is currently bootstrapped. For servlets, I strongly recommend looking into the Guice-Servlet extension which lets you define your servlets and dependencies in "type-safe, idiomatic Java" instead of the web.xml file. Beautifully simple.
Or at this point would using a framework be just as complicated and messy?
As you can see above, you can slowly start injecting bindings (and providers) directly within an existing web app without changing your interfaces or calling patterns with ease. All that's left is the boostrapping... Just sneak the DI in there and let it slowly take over :)
精彩评论