9

I'm hard at work packaging up an API for public consumption. As such I'm trying to limit the methods that are exposed to only those that I wish to be public and supportable. Underneath this of course there are a multitude of limited access methods.

The trouble is that I have a lot of internal code that needs to access these restricted methods without making those methods public. This creates two issues:

  • I can't create interfaces to communicate between classes as this would make these my internal methods public.
  • I can't access protected or default methods unless I put the majority of my internal classes in the same package.

So, I have around 70 or 80 internal classes in cleanly segregated packages BUT with overly permissive access modifiers. Would you say that a single package is the lesser of two evils or is there a better way to be able to mask my internal methods whilst keeping more granular packages?

I'd be interested to find out the best practice here.

I'm already aware of This

Chris
  • 4,200
  • 3
  • 35
  • 46
  • I think the question is subjective a bit: I, personally, don't like design decisions which lead to poor internal organization, like your all-classes-in-one-package case. But the right way of doing such things does not come up in mind right now. – BorisOkunskiy May 29 '10 at 07:36

3 Answers3

7

There are two solutions to your question that don't involve keeping all classes in the same package.

The first is to use the Friend Accessor/Friend Package pattern described in (Practical API Design, Tulach 2008).

The second is to use OSGi. There is an article here explaining how OSGi accomplishes this.

Related Questions: 1, 2, 3, and 4.

Community
  • 1
  • 1
Jeff Axelrod
  • 25,625
  • 29
  • 137
  • 239
  • This is an excellent answer and the link to friend package was just the solution I was looking for. – Chris May 28 '11 at 06:11
  • I'm glad I could help. I too needed a solution to this seemingly serious limitation of Java. This pattern should be an ideal candidate for extending Java using Jetbrains MPS or other model-driven development tool. – Jeff Axelrod May 28 '11 at 16:09
  • Well, you still have to make the internal abstract Accessor public (thus a part of API). Worse, an evil user can provide his/her own AccessorImpl, thus forcing your internal package to use his/her own API instead. This will work until the Item class gets initialized and fires the IllegalStateException. – charlie Oct 18 '12 at 13:55
2

One example could be the Servlet API as you see they have separated common servlet API and http into two packages.

If you separate your code in an api.jar and implementation.jar you could use interfaces for your implementation which are not visible to the users of api.jar. If objects of classes regardless of their package, have to collaborate in any way, the methods of course must be visible (at least in the implementation).

stacker
  • 64,199
  • 27
  • 132
  • 206
  • +1 Thanks for the information. I am aware that I can package the problem away but really I wish to restrict users that have both the api and implementation jars from being able to access non-interface implementation methods in a way not intended. – Chris May 28 '11 at 06:14
1

yeah, you just cannot protect internal implementation stuff from access. There are some technologies (like OSGi), which offer solution to that during runtime/application startup. This is a java language-design modularization flaw (but also very difficult to add afterwards due to down-compatibility).

I like the convention to add publicly visible artifacts to /api package and internals to /internal. In the end you end up with.


# this package is allowed to be accessed by api-users
com.foo.users.api
# this is completely internal logic (api implementation)
# should only be touched by the api-module itself.
com.foo.users.internal

This way you have clean separation and can also run static code analysis code-rules.

Like with servlet-api mentioned above you can even further split api vs. impl to different jars. But this involves more effort to build-lifecycle and maintaining modules, so I would only do it where a complete runtime artifact split make sense (like in jsr-spec vs. impl).

manuel aldana
  • 13,184
  • 8
  • 40
  • 49