Your mind is a terrible profiler
Today I found the following interface while doing a code review:
interface DeliveryListener {
void deliveryFulfilled(Delivery delivery);
void deliveriesFulfilled(Set<Delivery> deliveries);
}
This code offends me because the first interface method is a special case of the second method. Why not just use a singleton list! When prompted, the code's author claimed that providing two methods was better:
The JVM wouldn't have to create a whole object just to invoke a method.
All implementations of the 2nd method will just for through the Set and invoke the first method anyway, so it doesn't cause the implementor to have to do additional work.
Creating a whole object is a waste of CPU
Your mind is a terrible profiler. The deliveryFulfilled
method would not be a hotspot in the application, so micro-optimizations like this one only serve to obfuscate the source code. Whenever you review code and the justification for design is "optimization", be very suspicious. Never trust any optimization that doesn't have a repeatable test that justifies its existence.
The second method invokes the first method anyway, so the cost is zero
If this was the case, then the calling code should just do the loop for you, and only the first method should exist. Leaking implementation details in the interface is a bad thing, because it forces the design.
As software engineers, we're paid to express business processes in Java code. Our business models should be as simple and straightforward as possible. Leave the optimizations for later, you'll do a better job.
# posted by Jesse Wilson
on Monday, July 30, 2007
0 comments
post a comment
Review: Toshiba HD-A20 1080p HD-DVD Player
When I bought my HDTV in February, I paid extra for 1080p so I could brag about having a higher resolution than my friends who all have 1080i TVs. But my ability to brag was theoretical until I could plug in a source that produced a 1080p signal. Naturally, that source was a HD-DVD player.
I bought Toshiba's 1080p player for $399. I could have also bought a 1080i version for $299 instead. Since I paid $100 more for 1080p, I would have expected the device to play movies at 1080p resolution out of the box. But playing 1080p content requires a HDMI cable which isn't included in the box. Instead, it comes with cheap RCA cables that don't do 1080p. I dashed out to Target to pick up a $40 HDMI cable.
I started up the player tried to configure it using its setup application. To my horror, one dialog showed my player was factory-configured to output a 1080i signal. Toshiba's really trying to prevent me from getting the high-end signal that I paid extra for! If the person setting up this system was anyone other than an AV-nerd such as myself this option would likely never be found and the player would never output its full potential.
Most of the setup app is nice but there are some screens that really need polish. Some of the advanced setup dialogs look like gross DOS applications. Yuck.
Inside the box there was a printed warning, "Install firmware updates before playing any movies". Fine - the player has an ethernet port so this should be easy. After fiddling with it for 20 minutes and getting repeated "Server not found" errors I gave up on the online upgrade. I followed the alternate instructions and downloaded an ISO from Toshiba's website onto my Mac and burned that to a blank CD. I restarted the HD-DVD player with the disc inside, causing it to apply the update. This took 30 minutes with very little feedback about what it was doing. I'm used to my Wii and my powerbook, which all provide convenient progress bars and throbbers whenever they're performing a long job. The HD-DVD player just makes me wait.
Eventually the update completed and it was time to watch movies! My wife Jodie bought
Planet Earth which is a visually stunning nature documentary. This movie is beautiful and it really makes my TV shine. It really made the expense and hassle of my new toy seem justified.
While watching Planet Earth I needed to pause it with the included remote control. What a trainwreck. This is the worst remote I've ever owned. There's way too many buttons in a giant grid making it nearly impossible to find the one I want in the dark! Remotes should be tactile!
After that movie I watched a bit of
Children of Men which looks awesome in hi-def. In one scene, there's newspapers in the background, and you can actually read them! The special features are cute and the menus overlay the playing movie, which is slick. Instead of stopping the movie to turn on spanish subtitles, I could do that while it's playing. I'll never do this but it sure is cool!
After the movies were watched, I needed to mail in the included rebate for 5 free HD-DVDs. Unfortunately, I can't pick any 5 free DVDs; I need to choose from Toshiba's list. I'm getting Apollo-13 and Dukes of Hazzard, films I probably wouldn't have bought or watched otherwise.
Overall, the HD-A20 is a mild let-down. The user-experience is terrible: the out-of-box experience, the remote, the setup and the rebates all suck. But the picture is absolutely beautiful and my movies look and sound of perfect. Hopefully in their next generation of players, Toshiba gets the details right.
# posted by Jesse Wilson
on Sunday, July 29, 2007
1 comments
post a comment
Refactoring to Guice: Part 3 of N
In
Part 1 and
Part 2, I removed several static calls in my pizza program. The services class now benefits from an object-oriented design. Replacing static with non-static methods allows for polymorphism and that helps in testing:
public class PizzaServices {
private final Oven currentOven;
private final Address storeAddress;
private final GeographyServices geographyServices;
@Inject
public PizzaServices(Oven currentOven,
@StoreAddress Address storeAddress,
GeographyServices geographyServices) {
this.currentOven = currentOven;
this.storeAddress = storeAddress;
this.geographyServices = geographyServices;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = geographyServices.getDirections(
storeAddress, customer.getDeliveryAddress());
if (directions == null || directions.getLengthInKm() > MAX_DISTANCE) {
throw new InvalidOrderException("Cannot deliver to , " +
customer.getDeliveryAddress());
}
int arrivalTime = TIME_TO_PREPARE
+ currentOven.schedule(TIME_TO_PREPARE, pizzas)
+ directions.estimateTravelTime();
Invoice invoice = Invoice.create(pizzas, directions.getLengthInKm());
return new Order(pizzas, invoice, arrivalTime, customer, directions);
}
}
Polymorphism and static factory methodsRegrettably, the code still has an outstanding static method, the factory method
Invoice.create()
. The implementation of this method looks up the store address statically:
class Invoice {
public Invoice(List<PizzaSpec> pizzas, int deliveryDistance,
Address storeAddress) {
}
static Invoice create(List<PizzaSpec> pizzas, int deliveryDistance) {
return new Invoice(pizzas, deliveryDistance, PizzaStore.getStoreAddress());
}
}
I made the store address injectable in
part 2 so I just need to get that injectable value here. I start by nesting a factory interface inside
Invoice.java
:
class Invoice {
...
interface Factory {
Invoice create(List<PizzaSpec> pizzas, int deliveryDistance);
}
}
I Implement that interface as an anonymous inner class within my module:
static class PizzaModule extends AbstractModule {
protected void configure() {
...
bind(Invoice.Factory.class).toInstance(new Invoice.Factory() {
@Inject @StoreAddress Provider<Address> storeAddressProvider;
public Invoice create(List<PizzaSpec> pizzas, int deliveryDistance) {
return new Invoice(pizzas, deliveryDistance, storeAddressProvider.get());
}
});
}
}
About the factory's implementationEverything bound via
.toInstance()
is injected by Guice when the
Injector
is created. That way, factories and providers can depend on services provided by other modules. Although constructor-injection is generally preferred, field injection is sufficient in this case.
Within the factory implementation, I bind a
Provider<Address>
rather than the address directly. This isn't strictly necessary because the address is a constant. But in general,
Provider
s should always be used within factories. This ensures that I always get the correct instance, even if it depends on scope or context. I always use
Provider
s within my implementations of
Factory
and
Provider
.
Using the injected factoryTo replace the static
Invoice.create()
method call with the factory, I inject it:
public class PizzaServices {
private final Oven currentOven;
private final Address storeAddress;
private final GeographyServices geographyServices;
private final Invoice.Factory invoiceFactory;
@Inject
public PizzaServices(Oven currentOven,
@StoreAddress Address storeAddress,
GeographyServices geographyServices,
Invoice.Factory invoiceFactory) {
this.currentOven = currentOven;
this.storeAddress = storeAddress;
this.geographyServices = geographyServices;
this.invoiceFactory = invoiceFactory;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
...
Invoice invoice = invoiceFactory.create(pizzas, directions.getLengthInKm());
return new Order(pizzas, invoice, arrivalTime, customer, directions);
}
}
Now I can test the
PizzaServices
class without first preparing the static call to
PizzaStore.getStoreAddress()
. The code makes no static method calls and Guice does the wiring.
Looking forward: Simplifying the factory's implementationI've had to write a lot of code to implement the
Invoice.Factory
anonymous inner class. This is the best design, but it's more complex than the static method call. Better code should be easier to write otherwise I usually get lazy and revert to the static factory.
In the next release of Guice, user-defined factory interfaces will be supported. We first annotate the
Invoice
constructor's injected parameter:
static class Invoice {
public Invoice(List<PizzaSpec> pizzas, int deliveryDistance,
@Inject @StoreAddress Address storeAddress) {
}
...
}
and then we can bind the factory directly to the class it constructs:
static class PizzaModule extends AbstractModule {
protected void configure() {
...
bind(Invoice.Factory.class).toFactoryFor(Invoice.class);
}
}
This is equivalent to our factory inner class, but much more concise. If we change the signature of the
Invoice
constructor to add or remove injectable paramters, we don't need to update the factory interface.
Guice uses Java's
dynamic proxy mechanism to implement the factory interface at runtime. It aggregates the factory method's parameters with injected parameters from the
Injector
to build the instance at create time.
If you can't wait for Guice 2.0, there's a similar API called
AssistedInject that works with Guice 1.1.
Series ConclusionNow I've removed all the static calls, and the
PizzaServices
class is perfectly polymorphic and totally testable. In this series, I've demonstrated constant injection, annotations and factory injection.
Replacing static, global code with testable polymorphic code is Guice's core competency. It allows me to decouple interface from implementation. It automatically wires my dependencies for me. If you want to embrace object-oriented design,
guicify your app!
# posted by Jesse Wilson
on Tuesday, July 17, 2007
0 comments
post a comment
Living APIs: Part 1 of N
Writing APIs is hard. There's several factors to balance and it's difficult to get it right the first time. Fortunately, often we don't! We're not all writing code for the JDK, where bad APIs can never be removed in order to retain strict compatibility.
Most of the APIs I write are used by only a handful of teams (including my own), and I only need to maintain compatibility with the most recent releases.
I'd like to make changes to the API without changing all callers at the same time. One example situation comes with distributed code ownership. I own some library that some other teams are dependent on. I don't have full access to their code in version control, preventing me from using IDE refactoring tools to make all the changes. When I change the API, I need to support both the old and new APIs simultaneously. This allows my API's users to migrate painlessly.
In this series, I intend to describe some strategies for changing an API without breaking calling code.
Renaming an Interface...public interface PizzaStore {
Address getAddress();
PizzaOrder createPizzaOrder();
}
Our customer has changed their menu to focus on salads and pastas, making my chosen interface name inappropriate. Some of the new stores don't even serve pizza:
public interface PizzaStore {
Address getAddress();
SaladOrder createSaladOrder();
PastaOrder createPastaOrder();
boolean isPizzaAvailable();
PizzaOrder createPizzaOrder();
}
I need to rename the interface. Unfortunately, it's used by dozens of classes, spread across several teams!
Shouldn't I just give-up and document that a PizzaStore
didn't necessarily sell pizza? No! The domain model is the core of the project, and it has to be perfect!Fortunately, renaming an interface in parts is mostly mechanical work. First, we create a copy of the
PizzaStore.java
code to
Restaurant.java
:
public interface Restaurant {
Address getAddress();
SaladOrder createSaladOrder();
PastaOrder createPastaOrder();
boolean isPizzaAvailable();
PizzaOrder createPizzaOrder();
}
Next, I remove all the methods in
PizzaStore
and make it extend my new
Restaurant
interface:
/** @deprecated replace with Restaurant, which is more general */
@Deprecated
public interface PizzaStore extends Restaurant {
// only methods inherited from Restaurant!
}
Analyzing the ChangeAny code that implements PizzaStore
will automatically implement Restaurant
, since the interface and all members are inherited.
Any code that uses PizzaStore
will still have access to all the Restaurant
methods and fields, since these are also inherited.
Extending an interface without adding new methods is essentially like creating an alias for that interface. I can submit these changes to version control immediately, and then change the callers incrementally.
Fixing the callers
Fixing the callers can be done without any urgency. But only after all callers are fixed can the old bad name be removed.
Now I can go through with my IDE and change all the users of PizzaStore
in bulk. This is where find-and-replace tools, refactoring plugins, and Jackpot are very helpful. I change parameter types and variable declarations from PizzaStore
to Restaurant
. I do not change return values, and add a few casts where necessary:
public class DeliveryService {
private PizzaStoreRestaurant defaultLocation;
public DeliveryService(PizzaStoreRestaurant defaultLocation) {
this.defaultLocation = defaultLocation;
}
PizzaStore getClosestStore(Address address) {
...
}
Delivery createDelivery(PizzaStoreRestaurant origin, Address destination) {
...
}
PizzaStore getDefaultLocation() {
return (PizzaStore)defaultLocation;
}
}
Once nobody is expecting a PizzaStore
as the return type I can change those as well and remove the ugly casts:
public class DeliveryService {
private Restaurant defaultLocation;
public DeliveryService(Restaurant defaultLocation) {
this.defaultLocation = defaultLocation;
}
Restaurant getClosestStore(Address address) {
return null;
}
Delivery createDelivery(Restaurant origin, Address destination) {
return null;
}
Restaurant getDefaultLocation() {
return defaultLocation;
}
}
Finally, I can change my implementors from having an implements PizzaStore
block to implements Restaurant
, and remove the PizzaStore
interface for good.
That's a lot of work!
Naturally, this is much more labour-intensive than ideal. It shows how getting the names right the first time will save time.
But there's very little reason to hang on to a bad name, even if there's a lot of code that already uses it. In an agile-world, the role and responsibility of a class will change over the lifetime of a project, and it's name should keep up.
By following this example and making changes in a backwards-compatible way, you make it easier for users to upgrade to your latest API - the code still compiles and it still works!
In a future post, I'll discuss some strategies for changing other API elements over time. I'll also rant about some Java language features that aren't well suited for such changes.
Update, July 16: added clarification as to why I cannot use IDE refactoring tools
# posted by Jesse Wilson
on Saturday, July 14, 2007
3 comments
post a comment
Find the RSS Feed for any blog!
Most blogging tools like
Blogger include an secret link to the page feed in your published HTML. You can only see it by viewing the HTML source of a blog page:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Public Object</title>
<link rel="alternate" type="application/atom+xml" title="Public Object - Atom"
href="http://publicobject.com/publicobject/atom.xml" />
<link rel="alternate" type="application/rss+xml" title="Public Object - RSS"
href="http://publicobject.com/publicobject/rss.xml" />
...
My browser knows to look for this link, and provides a link to the RSS version. Pages that have the feed link get a cute little blue RSS link in the address bar:
My newsreader also knows how to find an RSS link on any blog I throw at it, using its convenient "Discover Feeds" feature.
For application developersIf you're writing a webapp, consider providing RSS versions for your data! You can include the link right in the page head without cluttering up the layout with the
standard orange icon.
This applies to all kinds of webapps, not just blogging tools. For example, I work on an online billing system. I bet our customers would love RSS feeds for their monthly bills!
For Internet usersIf you're looking for a feed icon, it might be hidden in the HTML source. Your newsreader might even be able to detect the feed link for you!
# posted by Jesse Wilson
on Friday, July 13, 2007
0 comments
post a comment
Beans binding is getting simpler and better
I've kind of had a love-hate attitude towards the
JSR-295 Beansbinding project:
love: observable lists in the JDK
hate: the observable lists' events are not fine-grained
love: binding in the JDK
hate: it requires EL, a technique lifted from JSP to embed Java-like syntax in strings
Well today, I love the project much more. Shannon Hickey has posted his plan to introduce a Property interface and to simplify binding by formalizing it as simply a managed relationship between two properties.
This change is going to make the project great! And I think that Beansbinding is going to become very important beyond Swing thanks to this change. Great work, Shannon.
# posted by Jesse Wilson
on Monday, July 09, 2007
3 comments
post a comment
Refactoring to Guice: Part 2 of N
In
Part 1, I removed some of the static calls in my pizza program, leaving me with this code. It still has several
static calls that interfere with testability:
public class PizzaServices {
private final Oven currentOven;
@Inject
public PizzaServices(Oven currentOven) {
this.currentOven = currentOven;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = Geography.getDirections(
PizzaStore.getStoreAddress(), customer.getDeliveryAddress());
if (directions == null || directions.getLengthInKm() > MAX_DISTANCE) {
throw new InvalidOrderException("Cannot deliver to , " +
customer.getDeliveryAddress());
}
int arrivalTime = TIME_TO_PREPARE
+ currentOven.schedule(TIME_TO_PREPARE, pizzas)
+ directions.estimateTravelTime();
Invoice invoice = Invoice.create(pizzas, directions.getLengthInKm());
return new Order(pizzas, invoice, arrivalTime, customer, directions);
}
}
class PizzaModule extends AbstractModule {
protected void configure() {
requestStaticInjection(OrderPizzaAction.class);
requestStaticInjection(PizzaUtilities.class);
bind(Oven.class).toProvider(new Provider() {
public Oven get() {
return Oven.getCurrentOven();
}
});
}
}
Injecting @Named valuesToday I'll use injection and annotations to replace the static call to
PizzaStore.getStoreAddress()
. We could just bind the store address in the injector, but that could be confusing if there are multiple addresses in the program. The fix is to name this address with a binding annotation:
public class PizzaServices {
private final Oven currentOven;
private final Address storeAddress;
@Inject
public PizzaServices(Oven currentOven,
@Named("storeAddress") Address storeAddress) {
this.currentOven = currentOven;
this.storeAddress = storeAddress;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = Geography.getDirections(
storeAddress, customer.getDeliveryAddress());
...
}
}
And in the Module, we bind the named address using
annotatedWith
:
class PizzaModule extends AbstractModule {
protected void configure() {
...
bind(Address.class)
.annotatedWith(Names.named("storeAddress"))
.toInstance(PizzaStore.getStoreAddress());
}
}
Now we can test the
PizzaService
interface without a dependency on static methods in the
PizzaStore
class.
Removing Strings with custom annotationsIf the use of the String
"storeAddress"
upsets you, we can replace that name with a custom annotation. In the file
StoreAddress.java
, we create the annotation, which itself must be heavily annotated:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface StoreAddress {}
Now we can replace the String name from the constructor with our annotation:
@Inject
public PizzaServices(Oven currentOven,
@StoreAddress Address storeAddress) {
this.currentOven = currentOven;
this.storeAddress = storeAddress;
}
And update our module:
class PizzaModule extends AbstractModule {
protected void configure() {
...
bind(Address.class)
.annotatedWith(StoreAddress.class)
.toInstance(PizzaStore.getStoreAddress());
}
}
Replacing a static method with an instance methodOur
PizzaServices
class still has a big dependency on
Geography.java
for its
getDirections
method. Fortunately, we already know how to do this - just as we swapped
PizzaUtilities
with
PizzaServices
in
Part 1, we can replace
Geography
with
GeographyServices
here. Then we can inject that into our
PizzaServices
class:
public class PizzaServices {
private final Oven currentOven;
private final Address storeAddress;
private final GeographyServices geographyServices;
@Inject
public PizzaServices(Oven currentOven,
@StoreAddress Address storeAddress,
GeographyServices geographyServices) {
this.currentOven = currentOven;
this.storeAddress = storeAddress;
this.geographyServices = geographyServices;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = geographyServices.getDirections(
storeAddress, customer.getDeliveryAddress());
...
}
}
With the changes so far, we can create custom subclasses of
Oven
and
GeographyServices
to test
createOrder
without dependencies. This means that our test will run faster and provide no false negatives.
The biggest benefit I get from non-static, injectable code is that if I need to make changes to the implementation of
PizzaServices
, the edit-compile-execute cycle is dramatically faster.
In a future post, I'll improve this code by replacing the
GeographyServices
and
PizzaServices
classes with interfaces.
Part 3
# posted by Jesse Wilson
on Wednesday, July 04, 2007
0 comments
post a comment
Refactoring to Guice: Part 1 of N
In this
N-part series, I'm attempting to document some patterns for improving your code with
Guice. In each example, I'll start with sample code, explain what I don't like about it, and then show how I clean it up.
The bad codeThey're everywhere! Static utility methods that depend on lots of stuff, which must in turn be
static:
public class PizzaUtilities {
private static final int TIME_TO_PREPARE = 6;
private static final int MAX_DISTANCE = 20;
public static Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = Geography.getDirections(
PizzaStore.getStoreAddress(), customer.getDeliveryAddress());
if (directions == null || directions.getLengthInKm() > MAX_DISTANCE) {
throw new InvalidOrderException("Cannot deliver to , " +
customer.getDeliveryAddress());
}
int arrivalTime = TIME_TO_PREPARE
+ Oven.getCurrentOven().schedule(TIME_TO_PREPARE, pizzas)
+ directions.estimateTravelTime();
Invoice invoice = Invoice.create(pizzas, directions.getLengthInKm());
return new Order(pizzas, invoice, arrivalTime, customer, directions);
}
}
What don't I like about it?Static dependencies on
Geography
,
PizzaStore
,
Oven
and
Invoice
classes make it impossible to test this method without testing those methods. The consequences of that:
If PizzaStore
is slow to initialize, so is my test
I can't test this method until I have the code for Geography
, even if that class is being written by another team
I have to remember to call Oven.setCurrentOven()
in my test's setUp()
method, or the test fails at runtime.
If Invoice.create()
depends on an external service such as a payment processing service, my test fails if that service is not available.
A non-static version with the same methods
Create an alternate non-static class that delegates to its static counterpart:
public class PizzaServices {
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
return PizzaUtilities.createOrder(pizzas, customer);
}
}
Replacing static calls with non-static
Now wherever I have calling code that was calling PizzaUtilities
, I can replace it with an injected instance of PizzaServices
. For example, this:
class OrderPizzaAction {
public void order(HttpSession session) {
Customer customer = session.getCurrentCustomer();
PizzaUtilities.createOrder(getPizzaSpecs(), customer);
}
...
}
becomes this:class OrderPizzaAction {
private final PizzaServices pizzaServices;
@Inject
OrderPizzaAction(PizzaServices pizzaServices) {
this.pizzaServices = pizzaServices;
}
public void order(HttpSession session) {
Customer customer = session.getCurrentCustomer();
pizzaServices.createOrder(getPizzaSpecs(), customer);
}
}
Notice that this required me to change the constructor for OrderPizzaAction
. If you'd like to postpone changing the constructor until later, you can still use PizzaServices
by statically-injecting the PizzaServices
instance:class OrderPizzaAction {
@Inject public static PizzaServices pizzaServices;
public void order(HttpSession session) {
Customer customer = session.getCurrentCustomer();
pizzaServices.createOrder(getPizzaSpecs(), customer);
}
}
And then in our module, we prepare the static injection:class PizzaModule extends AbstractModule {
protected void configure() {
requestStaticInjection(OrderPizzaAction.class);
}
}
Static injection is very helpful as a transitional aide while refactoring from static to non-static code.
One benefit of this work is already available. OrderPizzaAction
can now be tested without the Geography
etc., by passing in a mock subclass of PizzaServices
that overrides createOrder
.
Moving logic to the non-static version
Next, let's move the implementations logic from PizzaUtilities
to PizzaServices
. We'll leave a forwarding method in place in PizzaUtilities
so we don't have to update all the callers right away:public class PizzaUtilities {
@Inject public static PizzaServices pizzaServices;
public static Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
return pizzaServices.createOrder(pizzas, customer);
}
}
public class PizzaServices {
private static final int TIME_TO_PREPARE = 6;
private static final int MAX_DISTANCE = 20;
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = Geography.getDirections(
PizzaStore.getStoreAddress(), customer.getDeliveryAddress());
...
return new Order(pizzas, invoice, arrivalTime, customer, directions);
}
}
Notice that we'll used static injection to make our PizzaServices
instance available to PizzaUtilities
, so we'll need to update the module to prepare that:
class PizzaModule extends AbstractModule {
protected void configure() {
requestStaticInjection(OrderPizzaAction.class);
requestStaticInjection(PizzaUtilities.class);
}
}
Injecting the non-static version
Now that PizzaUtilities
is injected, we can start to inject its dependencies into it. The low-hanging fruit is the Oven.getCurrentOven()
singleton. We'll bind that in the module and then we can inject it!public class PizzaServices {
private final Oven currentOven;
@Inject
public PizzaServices(Oven currentOven) {
this.currentOven = currentOven;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
...
int arrivalTime = TIME_TO_PREPARE
+ currentOven.schedule(TIME_TO_PREPARE, pizzas)
+ directions.estimateTravelTime();
...
}
}
and then in the Module:class PizzaModule extends AbstractModule {
protected void configure() {
requestStaticInjection(OrderPizzaAction.class);
requestStaticInjection(PizzaUtilities.class);
bind(Oven.class).toProvider(new Provider() {
public Oven get() {
return Oven.getCurrentOven();
}
});
}
}
This means that whenever an Oven
instance is injected, it'll use the old Oven.getCurrentOven()
method to get it. Later on, we'll be able to remove that method as well.
Now we can test PizzaServices
without a particular Oven
instance prepared in advance. In a future post, I'll demonstrate how to remove the remaining static calls.
Part 2
# posted by Jesse Wilson
on Tuesday, July 03, 2007
1 comments
post a comment
Language Expressiveness and Complexity
Recently I've seen
comments about how language complexity is a Bad Thing that Must Be Avoided. I disagree. The expressiveness of a language influences what you can say with it. This applies to natural languages, programming languages, even programming APIs! When the language gets more complex (by adding new words or structures), the stuff you can say in that language becomes simpler.
Natural LanguagesIn German, the word schadenfreude means taking pleasure from other's pain. We need a word like this in English! When something terrible happens to Paris Hilton or Tom Cruise, we revel in it. But English speakers cannot concisely describe this satisfaction!
What's a website that gets updated regularly? With posts? It's a blog. It's a made-up word that neatly describes a concept. Attaching a name to the concept makes it more real. Adding a word to English makes English harder to learn, but more powerful to use.
Programming Languages
Autoboxing and varargs in Java 5 makes it easier to express my intention without the mechanics. Compare this:List smallPrimes = Arrays.asList(new Integer[] {
new Integer(3), new Integer(5), new Integer(7), new Integer(11) });
to this:
List<Integer> smallPrimes = Arrays.asList(3, 5, 7, 11);
Language-integrated query (LINQ) in C# lets you manipulate collections declaratively rather than programatically. I describe what I want from the collection without the gory details on how to get it!
APIs
The Future interface introduces a new concept: a return value that isn't available immediately. Using Futures allows client code to parallelize work without the clutter of managing threads.
Instead of manually managing the enabled state of the attached buttons and menus, the Action interface neatly encapsulates related behaviours. This makes the button API bigger and the client code smaller.
We can certainly get around with simpler languages. But I don't think that language simplicity should be preferred over language expressiveness. Java Closures give us a huge amount of expressivity for a small amount of complexity.
Footnote: C++
When anyone makes a case against language complexity, C++ is their canonical example of what not to do. The fallacy of this argument is that much of the complexity in C++ is accidental complexity. If done carefully, adding new features to Java will not harm the language.
# posted by Jesse Wilson
on Sunday, July 01, 2007
7 comments
post a comment