//a really dumb message sending class public class MessageSender { public void sendMessage(String message) { System.out.println(message); } } //another dumb class that uses the message sender public class MessageUser { //a reference to the sender object private MessageSender messageSender; //the message sender object is created in the constructor public MessageUser() { this.messageSender = new MessageSender(); } //send the message via the sender public void sendMessage(String message) { messageSender.sendMessage(message); } } //the main class that uses the message classes public class MessageMain { public static void main(String[] args) { MessageUser messageUser = new MessageUser(); messageUser.sendMessage("This is a test"); } }
OK, this is really mundane, but can see that MessageUser cannot use any other way of sending messages. If you want to use another means, say email, you'd have to either change what MessageSender does or change MessageUser to use a different class, call it EMailSender. However, we can now use an interface instead of a class:
public interface MessageSender { public void sendMessage(String message); } //an implementation of the interface public class SystemMessageSender implements MessageSender { public void sendMessage(String message) { System.out.println(message); } }
You still have to change MessageUser, but to use an interface given to, or "injected" into, the MessageUser object via the constructor:
public class MessageUser { //a reference to the interface private MessageSender messageSender; //the interface is sent to the object using the constructor public MessageUser(MessageSender messageSender) { this.messageSender = messageSender; } public void sendMessage(String message) { messageSender.sendMessage(message); } }
This works if we then, in the main class, create the object that implements the MessageSender and pass it to, or inject it into, the MessageUser object:
public class MessageMain { public static void main(String[] args) { SystemMessageSender messageSender = new SystemMessageSender(); MessageUser messageUser = new MessageUser(messageSender); messageUser.sendMessage("This is a test"); } }
Now if we want to have MessageUser send emails we create a class which also implements the MessageSend interface:
//an implementation of the interface public class EMailMessageSender implements MessageSender { public void sendMessage(String message) { EMail.textMessage(message); } }
All that then has to be done is create an object of this class in the main class and then pass it to MessageUser, as before:
EMailMessageSender messageSender = new EMailMessageSender(); MessageUser messageUser = new MessageUser(messageSender); messageUser.sendMessage("This is a test");
This technique, known as Dependency Injection, is quite an important design pattern and is mandatory in certain frameworks, such as Spring.
Introducing Google Guice
So far, so good, and it's difficult to see how this can be really improved upon. However, Guice (pronounced with a J rather than a G) does for dependency injection what Mockito does for unit testing. To introduce Guice into the above example, we have to create another class, an extension of Guice's AbstractModule class:
public class MessageModule extends AbstractModule { protected void configure() { bind(MessageSender.class).to(SystemMessageSender.class); } }
You can sort-of see what's going on. The module is responsible for creating the class that implements the interface, so whenever the interface is used, the object is bound to it. The magic happens in the Injector, used in the main class:
Injector injector = Guice.createInjector(new MessageModule()); MessageUser messageUser = injector.getInstance(MessageUser.class); messageUser.sendMessage("This is a test");
Notice that the module hasn't been told about MessageUser, so the Injector is figuring out it's dependencies from the constructor of the class, and this also has to change using the @Inject annotation:
@Inject public MessageUser(MessageSender messageSender) { this.messageSender = messageSender; }
Now all this doesn't seem like a big deal, if anything we've added lines and classes, but if you've got a lot of classes with umpteen dependencies, Guice can save you a lot of work.
No comments:
Post a Comment