-
Object Services, or bridging anemic and rich models, in CDI/Weld
Posted on May 27th, 2010 3 commentsRich domain models are certainly a nice, object-oriented idea, but I always had one problem with them: what if they become bloated with completely unrelated methods? For objects that are frequently used in a system, we may want to add various methods, which depend on the actual class of the object. Also, what if we’d like to use some other (e.g. CDI) beans as part of the method logic? Normally in DI frameworks there’s no injection into model classes. Or we want to add a frontend-specific method, but we receive the instances from a backend service?
Object Services try to address the issues above. Suppose we have a simple class hierarchy of animals:
abstract class Animal class Elephant extends Animal class Ant extends Animal
and we want to implement a
paintmethod, which paints a picture of the given animal on a canvas. Quite obviously, painting an elephant is different from painting an ant. There are several solutions:- add a paint method to the
Animalinterface – problems outlined above - add a paint method, in which we check which
Animalwas passed usinginstanceof– quite ugly - use the visitor pattern – typesafe, but quite verbose
I think the best solution would be to have type-safe “polymorphic extension methods”, so that in your code you could just add some methods to each class in a hierarchy, but unfortunately this isn’t supported by any Java (see also multiple dispatch).
Another possibility is to use what I call “Object Services”. If we want to add some methods to a class hierarchy, we create a parallel hierarchy of “services” (which are normal classes):
interface PaintService<T extends Animal> extends OS<T> { void paint(Canvas c); } // Implements OS<Elephant> class ElephantPaintService implements PaintService<Elephant> { // Here we can store the object, for which the service was invoked void setServiced(Elephant e) { ... } void paint(Canvas c) { ... } } // Implements OS<Ant> class AntPaintService implements PaintService<Ant> { // Injection works normally @Inject AnthillService anthill; void setServiced(Ant a) { ... } void paint(Canvas c) { ... } }OSis an interface marking some beans as object services; the class, to which the service corresponds is given as a type parameter.The
ObjectServiceExtensionwill detect all beans that implement theOSinterface, and register anOSP(Object Service Provider) bean which can be later injected to obtain a correct object service given anAnimal:@Inject OSP<Animal, PaintService<Animal>> paintService; void paint(Animal a, Canvas c) { paintService.f(a).paint(c); }Each invocation of the
fmethod will lookup the correct bean, based on the run-time type of the object passed, create a new instance of the found bean and set the object, for which the method was called. All beans created are CDI-managed, so injection etc works normally.void test(Canvas c) { // Will invoke paint(c) in AntPaintService paint(new Ant(), c); // Will invoke paint(c) in ElephantPaintService paint(new Elephant(), c); }The source code is available on GitHub in the cdiext project. To use it, just bundle the jar with your application.
Thanks to Tomek SzymaĆski for discussing the implementation.
So what’s next? The code could use a couple of improvements, but the biggest next task is to add deploy-time checking if there’s an object service for each class in a hierarchy (e.g. we have a
PrintServiceand anElephantPrintService, but forget to add anAntPrintService).Adam
2 responses to “Object Services, or bridging anemic and rich models, in CDI/Weld”

-
Interesting approach. This is similar to the the adapter pattern used throughout the Eclipse code base. A good overview of Eclipse adapters can be found at: http://www.eclipse.org/resources/resource.php?id=407
1 Trackbacks / Pingbacks
-
[...] Scala’s implicits it’s possible to implement Object Services in a much more “user-friendly” way. Just to remind, the goal is to extend a class [...]
Leave a reply
- add a paint method to the

DT June 2nd, 2010 at 15:45