Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Further into CDI

DZone's Guide to

Further into CDI

· Java Zone
Free Resource

Managing a MongoDB deployment? Take a load off and live migrate to MongoDB Atlas, the official automated service, with little to no downtime.

In one of my latest articles on CDI, one of the lessons I learned was to use factory methods in order to reduce the number of classes. In this article, I will go into the detail since I think it is of some importance. More basic informations on CDI can be found in some previous posts: overview part 1 and overview part 2.

The naive approach

During school, you were probably taught to create a class for each component. For example, if your application has 4 buttons, North, East, South and West, you’ll have 4 classes. Each class configures its label, its icon and its action, either in the constructor or in some other method.

Note: in CDI, your can annotate such method with @PostConstruct in order for the container to call it just after the constructor.

This produces the following code, when taking Swing as an example:

public class NorthButton extends JButton {

public NorthButton() {

// Initializes label
// Initializes icon
// Initializes action
}

...
}

Common behaviour and attributes may be factorized into one superclass, thus you end up with 5 classes.

Using the right number of classes is very important in any object-oriented language:

  • If you use too few classes, you run the risk that each class you develop has too many responsabilities and are too big.
  • On the contrary, if you use too much classes, you’ll explode code into many places and it will be a maintenance nightmare. Moreover, in the Sun (Oracle ?) JVM, classes are loaded into the PermGen space which has a fixed size. Who has never seen the dreaded java.lang.OutOfMemoryError: PermGen space?

IMHO, both are good reasons not to create a single class for each instancied component.

The factory approach

The second logical step is to create methods that produce the right instance. In our previous example, that would mean a factory class, with 4 methods to produce buttons, one for each button and probably a 5th method with common behaviour:

public class ComponentFactory {

@North
@Produces
public JButton createJButton() {

JButton button = new JButton();

// Set label
// Set icon
// Set action

return button;
}

...
}

Notice that you’ll still need an annotation for each single component:

@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,METHOD,PARAMETER,TYPE})
@Qualifier
public @interface North {}

In our case, that makes @North, @East, @South and @West. If these components are found throughout your application, fine (such as a Cancel button). If not, you still have a class for each component (annotations are transformed into classes at compile-time).

The “generic” annotations approach

CDI let you qualify injection not only from the annotation type but also from some (or all) their elements. We can thus greatly diminish the number of needed annotations. In order to do this, just create an annotation on this model:

@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,METHOD,PARAMETER,TYPE})
@Qualifier public @interface TypeName {

Class value();

String name(); }

Now the factory method becomes:

public class ComponentFactory {

@TypeName(value = JButton.class, name = "North")
@Produces public JButton createJButton() {

JButton button = new JButton();

// Set label
// Set icon
// Set action

return button;
}

...
}

Although this method meets our requirements regarding the number of classes (and annotations), it still has one major drawback: now, it’s the number of methods that is vastly increased. Though not nearly as great a disadvantage as a great number of classes, it makes the code less readable and thus less maintainable.

The fully “generic” approach

CDI let you use an injection point parameter for producer method. This parameter has much information about, guess what… the injection point. Such informations include:

  • the reflection object, depending on injection’s type, respectively Field, Method and Constructor for field, method parameter and constructor parameter injection
  • the required type to inject
  • annotations relative to the injection point

The latter point is what drives what comes next. It means you can annotate your injection point, then get those annotations during producer method execution. In our case, we could have an annotation for each attribute we want to set (text, icon and action). Let’s create such an annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,METHOD,PARAMETER,TYPE})
public @interface Text { String value(); }

Such annotation could be used in the producer method in the following manner:

public class ComponentFactory {
@Produces
@TypeName(JButton.class)
public JButton createJButton(InjectionPoint ip) {

JButton button = new JButton();

Annotated annotated = ip.getAnnotated();

if (annotated.isAnnotationPresent(Text.class)) {

Text textAnnotation = (Text) annotated.getAnnotation(Text.class);

String text = textAnnotation.value();

button.setText(text);
}

// Set icon
// Set action

return button;
}
}

Now, the only thing you have to do is annotate your button field like so:

public class Xxx {

@Inject
@TypeName(JButton.class)
@Text("North")
private JButton northButton;

...
}

This will get us a button with “North” as label. And nothing prevents you to do the same with icon and action.

Morevover, with just text, you could go further and manage internationalization:

@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,METHOD,PARAMETER,TYPE})
public @interface Text {

// Key in the properties file
String value();

// Path to the resource bundle
String resourceBundle() default "path/to/resource/bundle";
}
This annotation could also be reused of other components that manage text, such as labels.

Conclusion

There’s no right or wrong approach in using CDI. However, the latter approach has the merit of being the most powerful. Additionally, it also let you write code relative to a component type in one single place. This is the road I’ve taken so far, and I’m very satisfied with it.

From http://blog.frankel.ch/further-into-cdi

MongoDB Atlas is the easiest way to run the fastest-growing database for modern applications — no installation, setup, or configuration required. Easily live migrate an existing workload or start with 512MB of storage for free.

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}