Reputation: 10539
When I'm writing a new component with DDD I try to have one single public class in a package with all collaborators of my component package protected.
That usually plays out very nicely, because there's only one single entry point, that clients of my component can use. All (internal) collaborators will be hidden.
Consider an example, where a client can drive around with my purpose built Java car. I don't want the client to see all internal parts of the vehicle, so only the Car
class is exposed as public:
org.automobile
|- Car <-- only Car is public
|- Engine <-- all collaborators have default (package-protected) visibility
\- Battery
Now, things usually get more complicated as the domain evolves, so I want to move classes with their collaborators to subpackages to keep my structure nice and clean:
org.automobile
|- Car
|- drive.Engine <-- how to make this only available to Car
|- drive.Spark
|- drive.Belt
|- electric.Battery <-- and this
\- electric.BatteryCell
I would now like to make Engine
and Battery
visible to Car
only, because the client of my component should still not be able to use these classes directly.
I know that there's no "subpackage" visibility in Java, but is there any other lightweight pattern of achieving what I want, short of using Java 9 modules?
Upvotes: 4
Views: 1854
Reputation: 2605
I would say there are 2 options. Anyone feel free to chip in as this is question is interesting.
Use a single file for java code instead of packages
The idea is that you can use a single java file to declare the classes and API that are available to the Car.java (Engine and Battery). Classes declaring concepts hidden from Car (Spark, Belt, BatteryCell) should be private and static so that they do not have implicit links with the declaring class.
This approach can scale but requires more work. It is also a be helpful up to a point, it is not suitable for large code bases nor for code bases that will scale in the future to be large.
org.automobile
|- Car
|- Engine.java <-- how to make this only available to Car
|- Engine.java-Spark <--private static class in File Engine.java
|- Engine.java-Belt <--private static class in File Engine.java
|- Battery.java <-- package private class
\- Battery.java - BatteryCell <--private static class in File Battery.java
Combine the use of abstract and protected modifiers together with subpackages
The idea is that you create public Abstract classes whose functionality is exposed by protected methods. For instance AbstractBattery and AbstractEngine. Then you can create some classes that act as "proxies", you extend the abstract classes with package private implementations in the package of Car and override the methods that you want to make available. For example Engine and Battery. Car will have access to the overridden methods of the abstract classes. Another side-effect is that you cannot have final API methods in your AbstractClasses.
This approach is more suitable for large code bases and aims to hide functionality and abstractions from unintended use. However it requires some more work. Of course it is still possible for others to extend AbstractBattery and AbstractEngine and use them.
org.automobile
|- Car
|- Engine <-- extends AbstractEngine, is package private, overrides methods that Car needs to access
|- Battery <-- extends AbstractBattery,is package private, overrides methods that Car needs to access
|- drive.AbstractEngine <-- Public abstract class with no public methods, methods that are needed by Engine and Car are protected.
|- drive.Spark <-package private
|- drive.Belt <-package private
|- electric.AbstractBattery <--Public abstract class with no public methods, methods that are needed by Battery and Car are protected.
\- electric.BatteryCell <-package private
Here is an example of how this could look like:
org.automobile.engine.AbstractEngine
package org.automobile.engine;
public abstract class AbstractEngine {
protected void startEngine(){
System.out.println("Zoom Zooomm Zoommm");
}
}
org.automobile.Engine
package org.automobile;
import org.automobile.engine.AbstractEngine;
final class Engine extends AbstractEngine {
@Override
protected void startEngine(){
super.startEngine();
}
}
Upvotes: 1