I want highly comprehensible architecture, open to change and evolution. There are a lot of principles and practices that drive this goal, all well documented in the literature. I’m looking for a clear and simple implementation pattern that helps achieving the goal. Something that can be described in a single blog entry and that can be combined with the existing patterns.
Before revealing the actual implementation let’s zoom into somewhat underrated element of java: package-protected types. They are not used very often from my experience. Some engineers (including myself) use package-protected methods and constructors occasionally to simplify testing. Traditionally, all classes are public. Usually new classes are created public without too much thinking. IDEs generate new classes with ‘public’ identifier by default. In Groovy, classes are public by default (I still like Groovy a lot :). In general, I find all that quite disturbing. Package-protected types are lovable and they can be used to drive great object oriented design. It starts from package design.
Java package is a beautiful software design tool. Thinking hard about the contents of the package, the couplings between packages, knowing and eliminating package cycles, even choosing the right names and hierarchy for the package structure is one hell of a design tool that can significantly contribute to the quality of the architecture. When the package design is close to heart, the architecture can be comprehended literally by zooming in and out to the package structure.
I like to model java packages as software components. A component has the public API and the implementation details. The former is the official way of using the component. The latter is the stuff that we don’t want to leak to the consumers of the component. Standard practice in software component design is that the public API is a set of interfaces that neatly hide the implementation details. This approach allows evolution of the library in a way it is safe for consumers (compatibility) and convenient for maintainers (open to improvements). This is also fundamental for extensibility of the component’s API. What I suggest is to apply practices of component design, on a smaller scale, to the java package design.
The implementation may go like this: public concrete classes are discouraged. The only public types a java package can export are interfaces. If none of the classes are public how can the package be used "outside"? One approach is to relax the rule and allow some classes of the package to be public. There could be a single public class in the package that acts like a entry point to the features provided by the component. The entry-point class can use public interfaces to avoid leaking the internal implementation. Since majority of classes are package-protected, we have also the compiler and the IDE support informing what can be used from the component.
There’s also a puristic approach - it is actually possible to meet the ‘no public concrete classes’ requirement. Interfaces can have constants and therefore it is possible to expose component’s features without compromising visibility of any of the concrete classes:
//public interface exported by the package:
public interface HealthServices {
//constant that exposes features but hides the impl
public final static HealthServices API
= new DefaultHealthServices();
//impl class is package-protected
}
No doubt this model of designing of java code has limitations. Perhaps some other time I will write more about lessons learned from implementing this model and consequences on the architecture. I am super excited about this (though people tell me I get excited easily).
If after reading this you’re thinking more kindly of package-protected classes I’m already happy. If you’re more willing to start using classycle with your project I’m totally happy (in Gradle we apply use classycle.gradle script plugin). If you start considering java package design as an important part of architecture design I’m excited. If you try out some of the ideas or you have already used a similar approach let me know!
PS. I need ‘no public concrete classes’ t-shirt :)
2 comments:
Nice! never thought this way for hiding implementation!
An another approach is to use Spring:
you have only a public interface, a concrete class can be package-protected (Spring can initialise the package-protected class)
We are using this in our project and thanks to that we have a totally puristic approach on ‘no public concrete classes’ rule.
Post a Comment