Saturday, April 6, 2019

What is an interface and what are the advantages of making use of them in Java?

An interface in Java is a construct that allows a developer to define constants and completely abstract methods and all implementing classes will be required to provide an implementation of the defined methods.  The definition of an interface looks like the definition of a class except the keyword interface is used instead of class and instead of a method body, there is simply a semicolon denoting the end of the statement. For example, below is an interface named Foo that defines two methods that must be implemented by any concrete class that claims to be a Foo.

public interface Foo{
    public int bar();
    public int bar(int someArg);
}

The interface knows nothing of the implementation details of the concrete class that will implement the interface.  In the example below, the Foo interface does not even know of the existence of a FooImpl class which allows FooImpl to change implementation details without impacting the interface at all.

public class FooImpl implements Foo{
    private int value;

    public FooImpl() {
        this(0);
    }

    public FooImpl(int start) {
        this.value = start;
    }

    public int bar() {
        return this.value++;
    }

    public int bar(int someArg) {
        this.value += someArg;
        return this.bar();
    }
}

In some situations, an abstract class can accomplish the same goal as an interface, but it is important to note that in Java, a class is only allowed to extend a single base class, but it can implement any number of interfaces.  To understand why using interfaces may be a better solution, I would highly recommend reading Effective Java by Joshua Bloch in which he makes the point over and over again why composition is typically better than inheritance.

As described by Robert C. Martin (Uncle Bob) in his book Clean Architecture, the purpose of an interface is to invert the direction of a dependency.  Using the UserAuthenticationService from my previous post about Dependency Injection as an example, if UserAuthenticationService depended directly on a DatabaseUserRepository then every time the DatabaseUserRepository class changes then the UserAuthenticationService will need to be recompiled and redeployed.



By changing UserAuthenticationService to point to a UserRepository interface implemented by DatabaseUserRepository we effectively flip the direction of the dependency.



UserAuthenticationService depends on the UserRepository interface and DatabaseUserRepository depends on the UserRepository interface, but changes to DatabaseUserRepository no longer force changes to UserAuthenticationService.

Interfaces are crucial to the success of Spring.  Throughout the Spring Framework, interfaces are defined and often implemented by Spring with "opinionated defaults", but Spring allows developers to replace the provided implementation with their own via different configuration mechanisms.  By using interfaces, the Spring Framework does not need to be recompiled to accept one of these newly provided implementations and everything still works (assuming the interface was implemented according to the specs by the newly provided implementation).

The use of interfaces especially comes in handy when it comes time to test our software.  Operationally, our UserAuthenticationService may be configured to use a DatabaseUserRepository instance, but that may not work well for a unit test environment where we do not want to rely on the state of a shared database that may not be available.  In that case, UserAuthenticationService could be provided with a MockUserRepository that implements the UserRepository interface and stores configured values in memory.  This allows our unit tests to run in isolation, but the implementation of UserAuthenticationService does not need to change (or even know) that it's interacting with a different object.

Monday, April 1, 2019

What is dependency injection and what are the advantages?

Dependency injection is one form of Inversion of Control (IoC).  Instead of an object creating or being statically linked to implementations of a dependency, dependency injection allows dependencies to be provided to the object.

A simple example of a class that does not allow dependencies to be injected in any way is shown below.

public class UserAuthenticationService {
    private final UserRepository userRepository;

    public UserAuthenticationService() {
        this.userRepository = new DatabaseUserRepository();
    }

    public UserProfile authenticate(String username, String password) {
        return this.userRepository.findUser(username, password);
    }
}

The constructor creates its own instance of a DatabaseUserRepository. If a consumer of the UserAuthenticationService would like to use it with a different UserRepository, he/she does not have an opportunity to do so. Even if he/she did want to use the DatabaseUserRepository but wanted to pass additional arguments during construction (perhaps a non-default username/password), there is still no opportunity to make those changes without changing the implementation of the UserAuthenticationService.

There are multiple ways this problem can be rectified. The simplest way is to update the constructor to accept a UserRepository.

public class UserAuthenticationService {
    private final UserRepository userRepository;

    public UserAuthenticationService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserProfile authenticate(String username, String password) {
        return this.userRepository.findUser(username, password);
    }
}

Spring, like other frameworks, provides additional ways to inject dependencies.  Using the Spring provided @Autowired annotation or the JSR-330 provided @Inject annotation, Spring can inject dependencies into constructors, setter methods, and fields. An example of each is shown below with some thoughts on advantages and disadvantages of each.  Inject has been used instead of @Autowired because it gives a little more flexibility to switch between frameworks instead of being tied directly to Spring.

Constructor Injection
public class UserAuthenticationService {
    private final UserRepository userRepository;
    
    @Inject
    public UserAuthenticationService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserProfile authenticate(String username, String password) {
        return this.userRepository.findUser(username, password);
    }
}

With constructor injection, when Spring creates the UserAuthenticationService bean, it will inject an instance of a UserRepository bean from the application context.  Constructor injection has the advantage that the class can be used outside of a dependency injection framework without any changes, the injected dependency can be assigned to a final field preventing any code from accidentally or maliciously changing the value, and any setup that needs to occur after dependency injection but before the class can be used can take place in the constructor.  The drawback to this approach occurs if the service is created through Java config then the method that creates the UserAuthenticationService bean must have dependencies injected into it to pass along to the constructor explicitly.  As the number of the dependencies of the service increases, the number of arguments to the constructor will also increase as well as the number of arguments in the bean constructor method.

Field Injection
public class UserAuthenticationService {
    @Inject
    private UserRepository userRepository;
    
    public UserAuthenticationService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserProfile authenticate(String username, String password) {
        return this.userRepository.findUser(username, password);
    }
}

Field injection injects dependencies directly into a field of a bean after construction.  This method of dependency injection can be less flexible than constructor dependency injection because any initialization that must be done after dependencies are injected must be performed in a separate method.  A method annotated with @PostConstruct will be called automatically by dependency injection frameworks, but users not using dependency injection frameworks will have to know to call this method manually.  In addition, injected fields cannot be marked as final due to injection occurring after class construction.

Setter Injection
public class UserAuthenticationService {
    private UserRepository userRepository;

    @Inject
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserProfile authenticate(String username, String password) {
        return this.userRepository.findUser(username, password);
    }
}

Setter injection, as the name suggests, uses setters to receive dependency injections.  Like constructor injection, setter injection allows the class to be used outside of a dependency injection framework without any changes.  Unlike constructor injection, member variables cannot be marked as final due to the setter allowing mutation.  Setter dependency injection is very similar to field dependency injection except it gives a chance to perform additional logic after individual dependencies are injected (inside the setter method).  If any work should be performed after all dependencies are injected then a method annotated with @PostConstruct like that used in field injection will be needed.