Using Java Class Extension Library for Data-Oriented Programming
Discover an OOP approach to effectively separate data from domain-specific logic in data-oriented programming, utilizing the Java Class Extension Library.
Join the DZone community and get the full member experience.
Join For FreeThis article presents an object-oriented programming (OOP) approach to effectively separate data from domain-specific logic in data-oriented programming, utilizing the Java Class Extension Library.
Problem
Consider a scenario where we are building a warehouse application designed to handle the shipping of various items. We have established a hierarchy of classes to represent the goods we have:
class Item {}
class Book extends Item {}
class Furniture extends Item {}
class ElectronicItem extends Item {}
To implement shipping logic for each item, one might be tempted to add a ship()
method directly to each Item
class. While this is straightforward, it can lead to bloated classes filled with unrelated operations — such as shipping, storing, retrieving from a database, and rendering.
Instead of mixing these responsibilities, it’s better to keep items primarily as data classes and separate domain-specific logic from them.
Shipping Logic: Functional Style
To ship an item, we could define a method like this:
public ShippingInfo ship(Item item) {
if (item instanceof Book) {
return shipBook(item)
} else if (item instanceof Furniture) {
return shipFurniture(item);
} else if (item instanceof ElectronicItem){
return shipElectronicItem(item);
} else {
return null;
}
}
Alternatively, use modern switch statements with pattern matching:
public ShippingInfo ship(Item item) {
return switch (item) {
case Book book -> shipBook(book);
case Furniture furniture -> shipFurniture(furniture);
case ElectronicItem electronicItem -> shipElectronicItem(electronicItem);
};
}
While these methods are simple and direct, they come with several disadvantages:
- Breaks OOP principles: They mimic polymorphism in an outdated style.
- Violates SOLID principles: Adding a new
Item
class necessitates changes in the shipping logic. - Inconvenient and error-prone: Without dedicated
ship()
methods, shipping logic can become scattered and duplicated across the codebase, making it hard to alter it.
Shipping Logic: Class Extensions
A more effective solution involves introducing a Shippable
class extension (category) that encapsulates the ship()
method for items. Each Item
class would then have a corresponding Shippable
extension:
class Item (Shippable) {
public abstract ShippingInfo ship();
}
class Book (Shippable) {
public ShippingInfo ship() {}
}
With this design, shipping an item simplifies to a single call:
item.ship()
This approach is clean, convenient, extensible, and maintainable. However, Java does not natively support class extensions but it would be good to utilize that approach somehow.
Shipping Logic, Best Solution: Java Class Extension Library
The Java Class Extension Library provides a mechanism to mimic class extensions by finding matching extension objects and using them for extended functionality. So we can use it to implement the shipping logic.
The core of the library is the ClassExtension
class, which offers methods for dynamically finding and creating extension objects as needed.
We can create an Item_Shippable
class that acts as a Shippable
category. This class must implement the DelegateHolder
interface to allow it to work with items and provides a ship()
method. Then we should subclass Item_Shippable
and provide class extensions for each particular Item
class.
class Item_Shippable implements ClassExtension.DelegateHolder<Item> {
public ShippingInfo ship() {
return …;
}
}
class Book_Shippable extends Item_Shippable{
public ShippingInfo ship() {
return …;
}
}
Now, shipping an item becomes as simple as:
ClassExtension.extension(item, Item_Shippable.class).ship()
Shipping a collection of items is equally straightforward:
Item[] items = {
new Book("The Mythical Man-Month"),
new Furniture("Sofa"),
new ElectronicItem(“Soundbar")
};
for (Item item : items) {
ClassExtension.extension(item, Item_Shippable.class).ship();
}
It is possible to further simplify things by adding an extensionFor(Item)
helper method to the Item_Shippable
:
public static class Item_Shippable implements ClassExtension.DelegateHolder<Item> {
public static Item_Shippable extensionFor(Item anItem) {
return ClassExtension.extension(anItem, Item_Shippable.class);
}
...
}
With that helper method, shipping becomes simpler and shorter:
Item_Shippable.extensionFor(anItem).ship()
Supporting a new Item
class using the Java Class Extension library requires just adding a new Shippable
extension with a proper ship()
implementation. No need to change any other code. That is it.
Java Class Extension library provides a valuable alternative for class extensions with just a little more verbose code and a little more complex implementation.
Java Class Extension Library Details
Extension Classes
All extension classes must implement the DelegateHolder
interface and should follow the naming convention: OriginalClass_ExtensionName
, e.g., Shape_Drawable
.
Inheritance Support
ClassExtension
takes care of inheritance, allowing you to create a hierarchy of extensions that can mirror the original classes' hierarchy. If no explicit extension is specified for a class, its parent extension will be used.
Caching Mechanism
The library supports caching of extension objects using weak references to automatically release objects that are no longer in use. Cleanup can be performed manually using cacheCleanup()
, or you can schedule automatic cleanup via scheduleCacheCleanup()
. To stop automatic cleanup, call shutdownCacheCleanup()
.
About Java Class Extension Library
The library is free under the terms of the MIT License and available for download at GitHub.
By leveraging this library, developers can maintain a clean separation between data classes and domain-specific logic while adhering to OOP principles. This results in a flexible and extensible codebase that enhances maintainability and reduces errors.
Opinions expressed by DZone contributors are their own.
Comments