I am creating a Java library, as a final product in intend to distribute this .jar to developers.
I am "translating" my library from Objective-C where I control which classes header files are available to the developer. In other words I am only exposing to the developer a few classes they can handle.
In my Java library I am using packages and my package has grown quite big. So I decided to separate into different packages my models and controllers. But now the models I wanted to keep private I need to mark as public in order to use from the main package.
My question is does this go against what I was doing in Objective-C ?
For example I have an Event class which is really only used internally and I don't want the user to know about it or think about it. I have another class TimedEvent, which the user can get an instance of an manage.
In my Objective-C, I simply excluded Event class from the library public scope, allowing TimedEvent.
If I am making things more tidy in my library then it seems packages aren't the way. Since now, my main controller is in the main package and all the models are in another package - forced to have a public scope.
Opinions ?
This is possible with Java but there are reasons why (almost) no one does it...
If you put the implementation and the interface into the same package, then you can omit all access modifiers (private
, protected
, public
) from classes and methods to give them "default" or "package" visibility: Only classes in the same package are allowed to see/use them.
Drawback: You'll have to mix API and implementation.
The other approach is to move the implementation into a package *.private.*
. No more mixing of API and implementation but malicious users can easily access the implementation - it's just a naming convention. Like a STOP sign: It means something ("be careful") but doesn't actually stop you.
Lastly, you can implement the interface inside of the interface. For example:
public interface IFoo {
String getName();
private static class Foo implements IFoo {
public String getName();
}
public static class FooFactory {
public static IFoo create() { return new Foo(); }
}
}
Ugly, ain't it?
The common approach to controlling exposure of your classes to the world is hiding implementations behind interfaces and factories.
- Create an interface for your
TimedEvent
, and a class for creating instances of TimedEvent
interface
- Put the interface in the main package, and the factory in a sub-package
- Give the factory public visibility
- Implement the interface in the sub-package, giving it package visibility
- Create an instance of the class implementing the
TimedEvent
interface in the factory
Here is an example of how you can do it:
package com.my.main;
public interface TimedEvent {
void fire();
}
package com.my.main.events;
import com.my.main;
public class EventFactory {
public TimedEvent makeTimedEvent() { return new TimedEvent (); }
}
// TimedEventImpl has package visibility - it is not public.
class TimedEventImpl implements TimedEvent {
public void fire() {
// Fire a timed event
}
}
The users would access TimedEvent
like this:
com.my.main.events.EventFactory f = new com.my.main.events.EventFactory();
com.my.main.TimedEvent evt = f.makeTimedEvent();
evt.fire();