February 14, 2016

Java EE: Top 6 design patterns in practice (part 2 of 2)


Pages: 1 2

Singleton

Motivation: You want to create an object which exists only once.

Of course, we all know the classic Java implementation of the singleton pattern.
public class SystemConfig {
    private static class InstanceHolder {
        private static final SystemConfig INSTANCE = new SystemConfig();
    }
    
    public static SystemConfig getInstance() {
        return InstanceHolder.INSTANCE;
    }
    
    private SystemConfig() {}

    public String getContainerVersion() {
        ...
    }
    
    // other singleton methods...
}
However, this pattern is rightfully debated as being in fact an anti-pattern in many scenarios, as becomes apparent if we observe the client code:
public class Client {
    SystemConfig config = SystemConfig.getInstance();
    
    public void printInfo() {
        LOGGER.info(config.getContainerVersion());
    }
    
    ...
}
Its use of a global (static) field creates hard-wired dependencies in your code (that’s a violation of the dependency inversion principle DIP) and makes it hard to write tests (mocking!).

Java EE addresses this issue by providing an out-of-the-box facility to create singleton instances which happens to be much more concise as well. Just use the respective annotation:
@ApplicationScoped
public class SystemConfig {
    public String getContainerVersion() {
        ...
    }
    
    // other singleton methods...
}
In the client, use the default dependency injection mechanism to access the singleton. The Java EE container makes sure that only one instance of a singleton exists at any moment, and that it is thread-safe. With this implementation, we could easily swap the singleton instance (e.g. using an @Alternative declaration) or mock it in our tests.
public class Client {
    @Inject SystemConfig config;
    
    public void printInfo() {
        LOGGER.info(config.getContainerVersion());
    }
    
    ...
}
Note however that depending on the bean type you are using, you need to use different annotations to mark a bean as a singleton, and to refer to it, depending on the underlying bean definition standard:

Singleton Bean Type Singleton annotation Injection annotation Bean definition standard
- @javax.enterprise.context.ApplicationScoped @javax.inject.Inject CDI
@javax.ejb.Singleton @javax.ejb.Singleton @javax.ejb.EJB EJB
@javax.faces.bean.ManagedBean @javax.faces.bean.ApplicationScoped @javax.faces.bean.ManagedProperty JSF
@javax.inject.Named @javax.inject.Singleton @javax.inject.Inject Dependency injection for Java

I went into more details about the different Java EE bean definitions in a previous blog post.

For now please note: In Java EE, never build the singleton pattern by hand; always use the respective Java EE facility.

Factory

Motivation: You want to decouple the creator of an object from the object itself and its user.

I’ve seen terrible ideas of what a factory is and how it gets implemented in real-world projects, including the naïve assumption that simply any facility which returns an instance is to be considered a factory:
public class DiscountProducer {    
    public static Discount createDiscount(Customer customer) {
        ...
    }
}

public class Client {
    public void applyDiscount(Customer customer) {
        int discount = DiscountProducer.createDiscount(customer).amount;
    }
}
This is actually just a global function which returns an object. As with primitive singleton implementations (discussed above), globals are always a bad idea because they increase coupling and diminish testability.

In Java EE, we can realize abstract factories as an @ApplicationScoped singleton CDI bean (see above).
public interface DiscountFactory {
    public Discount createDiscount(Product product);
}

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Discounted {
    public DiscountType value() default DiscountType.DEFAULT;
}

@ApplicationScoped
@Discounted(DiscountType.EXTRA)
public class ExtraDiscountFactory implements DiscountFactory {    
    @Override
    public Discount createDiscount(Product product) {
        ...
    }
}

public class Client {
    @Inject
    @Discounted(DiscountType.EXTRA)
    private DiscountFactory discountProducer;
    
    public void applyDiscount(Product product) {
        int discount = discountProducer.createDiscount(product).amount;
    }
}
We can optionally have multiple factories implementing the same interface, and choose the desired factory using the @Alternative annotation (globally) or the @Qualifier meta-annotation (locally) to choose the desired factory at runtime.Through the use of dependency injection, this code is compliant to the dependency inversion principle (DIP).

Bottom line on factories: A static method doesn’t make a factory. In fact, never use static methods when dealing with information provided by a transactional / contextual resource (testability!). Also, you should have a good reason to really use the factory pattern at all rather than simply injecting / creating the instance you’re looking for. There is no “technical” need for factories in Java EE, and introducing them just “because reasons” clearly violates YAGNI (You ain’t gonna need it).

Façade

Motivation: You want to make a complex system accessible through a simple interface.

Okay, this one is probably even more simple than template method. You build an abstraction which internally uses several interfaces to other services. This is typically excessively used in the “service” layer of a Java EE application, for instance, to retrieve information from multiple other services.

The actual anti-pattern here rather lays in over-use of this pattern. It typically doesn’t make sense to create very narrow, specific interfaces just for one specific task. They are typically hard to document and maintain and may introduce a considerable amount of delegate-calls which add little value. Instead, try to  keep your service interfaces lean and self-documenting.

This typically happens if the project strives after a strongly “service-oriented” architecture with action-driven interfaces rather than an “object-oriented” architecture with resource-based interfaces (e.g. REST).

Consider this example of a service implementation:
public class AddressFacade {
    @Inject
    CustomerService customerService;
    
    @Inject
    AddressService addressService;
    
    public void updateAddressStreet(Long customerId, String street) {
        Customer customer = customerService.findById(customerId);
        Address address = customer.getAddress();
        address.setStreet(street);
        addressService.save(address);
    }
    
    public void updateAddressCity(Long customerId, String city) {
        Customer customer = customerService.findById(customerId);
        Address address = customer.getAddress();
        address.setCity(city);
        addressService.save(address);
    }
}
The overvalue of these “umbrella methods” is at least questionable. If we imagine that our services would consist of dozens of highly specialized methods, this may become a true maintainability threat, known as a violation of the interface segregation principle (ISP). This is especially true if all these methods just consist of a few ever-same invocations of underlying services, further violating the DRY (Donn’t repeat yourself) principle.

Also note that although we use container bean injection, interdependency between the beans potentially increases the more functionality a façade comprises.

In above example, the façade implementation further ignores the fact that we could cascade saving a Customer (including its address), hence not needing an explicit AddressService reference / invocation at all. I’ve actually observed in practice that lack of understanding of JPA’s cascade functionality has caused the introduction of superfluous façades. This hence may also indicate a YAGNI (You ain’t gonna need it) design violation.

In an extreme case, a whole additional abstraction layer is introduced, which typically hampers maintainability. A very common example is the introduction of an empty delegate “DAO” layer behind the service façade, a job which in Java EE 5+ is perfectly accomplished by the built-in EntityManager.

In the example use case, I would get rid of the façade altogether, and use a proper MVC pattern to let the user update the customer model (including its address properties). Then it’s enough to simply save the updated model on the persistence layer:
customerService.save(customer);
You should always strive after a clean and reusable service interface. Thinking in objects / REST / CRUD helps us achieve this goal.

Bottom line: Never build an abstraction just for the sake of the abstraction. If you need an abstraction in one place, don’t enforce building an entire delegate-layer if you don’t need it in other places.

Conclusion

Of the many patterns which have been described over time, we really only use quite a few regularly in typical Java EE projects. It’s ever more important to be able to identify opportunities for their usage and to apply them correctly.

These design patterns are so important especially in business software development, because:
  • They allow developers to use a common, abstract vocabulary to discuss and document software design (increasing software readability and maintainability).
  • They provide best-practice proven solutions to common problems (increasing robustness).
  • Their implementation is typically well-supported by software languages and frameworks (increasing ease-of-development).
Thank you for your interest. I shall probably address some other useful design patterns in future Java EE-themed articles. In the meantime, if you have any questions or if you’d like to propose corrections or additions, please post them in the comments section below.

Pages: 1 2

No comments:

Post a Comment