Separate Abstractions
by kirk knoernschild
Statement
Place abstractions and the classes that implement them in separate modules.
Description
You create an abstract class or interface to help reduce coupling between classes. This offers the ability to create new implementations of the abstraction without impacting clients dependent on that abstraction. In this sense, abstract coupling allows you to add new classes to your system without modifying existing classes, and it does so by reducing the dependency relationship between classes.
But coupling classes abstractly does not help reduce the dependency structure between components, especially if the abstraction and the classes that realize the abstraction reside in the same component. So while abstract coupling allows you to effectively reduce coupling between classes, it does not help to eliminate coupling between components. The abstract modules patterns is a prime example of this, where the client.jar module is still tightly coupled to the service.jar module even though the Client concrete class is not directly coupled to the ServiceImpl class.
The ability to deploy client.jar without service.jar does not exist given the structure in Figure 10.1.1. But what if I have a situation where I need to use client.jar without service.jar, or possibly with a new implementation. Even if I create new implementations of the Service interface, I’m still required to deploy service.jar with client.jar due to the placement of Service interface. In order to avoid this nasty problem, I can move the Service interface to the client.jar. This still allows me to configure the Client class with the appropriate Service implementation, yet the client.jar is not dependent on the service.jar module. In fact, the direction of the relationship has been inversed. Instead of client.jar depending on service.jar, the service.jar is now dependent on client.jar.
By simply changing the placement of classes, I’ve changed the dependency relationship between my modules. We saw an example of how module relationships can be inverted in the Module Relationships pattern Sample Code section. But inverting the dependency relationship may not always be what we’re interested in doing. Certainly, we know have the ability to use client.jar without service.jar, but can no longer use service.jar without client.jar. If we need the ability to use both client and service independent of each other, I need a different structure that accommodates this need. When this situation arises, the Separate Abstraction pattern can be used.
Fortunately, it’s as easy as moving the Service interface to a new component that both client.jar and service.jar modules are dependent upon. Figure 1 illustrates the new structure. In most cases where you take this approach, the new module will be an entirely abstract module. We refer to an abstract module as a specification module.

Figure 1: Separate Abstractions Pattern
Implementation Variations
SeparateAbstractions solves two different problems using a common technique. If there are two modules involved in the relationship, by separating the abstraction from the implementation, you are inverting the relationship between the two components. Using an ImplementationFactory will help maintain this separation. When more than three components are involved, SeparateAbstractions allows you to physically decouple two components where no relationship exists between the two components in either direction.
Consequences
Invariably more complex structure to manage.
The two key ingredients when you SeparateAbstractions is deciding where you should place the abstraction and where you should place the implementation. Since there could be many different implementations, they key to making this decision is figuring out your dependencies keeping in mind that the components containing the implementation will be dependent on the component containing the abstraction.
Since all implementations will only have a single abstraction, the placement of the abstraction is key to creating the component relationships that you want. In general, you want to keep the abstraction as close as possible to the classes that depend upon it. Here are some general guidelines to making this happen.
- If only a single class, or set of classes in the same package, rely upon the abstraction, then place the abstraction in that package.
- If a set of classes that span multiple packages all rely upon the abstraction, but all packages exist within the same component, place the abstraction in a separate package in that component.
- If a set of classes that span multiple components all rely upon the abstraction, and you want to keep each of those components independent of each other, then place the abstraction in separate component.
SeparateAbstractions allows you to eliminate a module relationship. The module depending on the abstraction needs to be injected with an implementation or have some way to lookup the implementation at run-time. Creation of the appropriate implementation is another important consideration. If a lookup approach is taken, the lookup mechanism should use reflection to avoid introducing the dependency. If injection is used, you’ll need a separate component that performs the creation. SeparateAbstractions is concerned with flexibility, but comes at the price of complexity.
When assembling components, you may find that two otherwise independent components share common state. You’ll likely want to avoid having each component initialize and manage that state separately due to overhead, and possibly unreliable processing. Doing so also incurs and unneccessary performance burden. If the state that each component relies upon is simply data that each requires to perform some processing, then it’s fairly easy to populate a bean defined by each component and pass the bean into the class requiring that data. But in situations involving more than state, each component may need to call back on an external entity. In this case, you cannot put the functionality on the bean since the bean would then require the ability to talk to the component. In order to accommodate this scenario, each component would need to define it’s own abstraction (the approach taken in Figure 10.1.2) or each component would need to depend on another component containing the abstraction (the approach taken in Figure 10.1.3).
Sample Code
In Abstract Modules, we depend upon the abstract elements of a module. In Implementation Factory, we used a factory to create the implementation class. Now, by moving the abstract class to a separate module, we can completely eliminate the relationship between the two modules.
Inverting the relationships allows us to deploy the customer.jar module independent of the billing.jar module. Again, it’s all about need. But I’d like to explore another option based on another important need – the ability to test and deploy modules independently. Before inverting the relationships, I am able to test and deploy the billing.jar module independently. After inverting the relationships, I can test and deploy the customer.jar module independently. But what if I want to test or deploy both modules independently? To do this, I need to completely eliminate the relationship altogether.
As it turns out, because I’ve got a pretty flexible class structure after I inverted the relationships (lot’s of abstract coupling), I can do this by simply bundling the two interfaces – Bill and DiscountCalculator – into a separate module. No other coding changes required. I start by moving them to a new package, which we’ll case base. (moving them to a new package called base). Then, I modify my build script (modify my build script) to bundle these two interfaces into a separate base.jar module, and we have successfully eliminated the relatinonship between the bill.jar and cust.jar modules. This is illustrated below in Figure 1.

Figure 1: Separating Abstractions to another Module