FriendPackages

From APIDesign

(Difference between revisions)
Jump to: navigation, search
Line 1: Line 1:
-
wikipedia::Object-oriented_programming_language|object oriented languages]] offer some kind of [[encapsulation]], which often takes form of having '''public''', '''protected''' and '''private''' access modifiers. Indeed, designers soon found that this is not enough and as such [[C++]] has ''friend'' constructs and [[Java]] adds additional ''package private'' access type.
+
Common [[wikipedia::Object-oriented_programming_language|object oriented languages]] offer some kind of [[encapsulation]], which often takes form of having '''public''', '''protected''' and '''private''' access modifiers. Indeed, designers soon found that this is not enough and as such [[C++]] has ''friend'' constructs and [[Java]] adds additional ''package private'' access type.
The problem with the [[Java]] approach is that it generally dictates the files layout of your friends. They need to be placed in the same directory. Many people do not like that, as this mixes the classes representing the API with the classes that implement them. And as common wisdom and [[Code Against Interfaces, Not Implementations|Chapter 8]] advice it is preferable to ''code against interfaces and not the implementation''. The [[Code Against Interfaces, Not Implementations|Chapter 8]] explains that this old good advice can have various meanings and take multiple forms. One of such forms is the practise of putting the implementation and interfaces sources in two distinct disk locations. This implies that the [[Java]]'s ''package private'' access modifier is useless for making these two sets of classes be friends.
The problem with the [[Java]] approach is that it generally dictates the files layout of your friends. They need to be placed in the same directory. Many people do not like that, as this mixes the classes representing the API with the classes that implement them. And as common wisdom and [[Code Against Interfaces, Not Implementations|Chapter 8]] advice it is preferable to ''code against interfaces and not the implementation''. The [[Code Against Interfaces, Not Implementations|Chapter 8]] explains that this old good advice can have various meanings and take multiple forms. One of such forms is the practise of putting the implementation and interfaces sources in two distinct disk locations. This implies that the [[Java]]'s ''package private'' access modifier is useless for making these two sets of classes be friends.

Revision as of 11:40, 24 October 2008

Common object oriented languages offer some kind of encapsulation, which often takes form of having public, protected and private access modifiers. Indeed, designers soon found that this is not enough and as such C++ has friend constructs and Java adds additional package private access type.

The problem with the Java approach is that it generally dictates the files layout of your friends. They need to be placed in the same directory. Many people do not like that, as this mixes the classes representing the API with the classes that implement them. And as common wisdom and Chapter 8 advice it is preferable to code against interfaces and not the implementation. The Chapter 8 explains that this old good advice can have various meanings and take multiple forms. One of such forms is the practise of putting the implementation and interfaces sources in two distinct disk locations. This implies that the Java's package private access modifier is useless for making these two sets of classes be friends.

This often leads people to open the APIs up and use public or protected modifiers, sometimes annotating the method with a javadoc note do not call me, I am part of the implementation. This is unfortunate as it pollutes the APIs with useless implementation details, distracts the API users and potentially creates security holes in the API.

One of the solutions is to add yet another modifiers to Java and introduce concept of friend packages. This may happen occasionally. Meanwhile he is little API design pattern that makes that possible now.

Friend Packages

Imagine you have an API class called Item with public getter and setter while its constructor and its addChangeListener methods are dedicated only for friends. Then just write:

does not exists: design.less.friend.Item

This is a regular Java code relying on the standard 'public and package private modifiers. No surprises so far, the next quest is to teleport the API to the friend package with implementation classes. This can be done by defining an accessor class in such package:

does not exists: design.less.friend.Accessor

This class defines an internal, friend API specifying what is the functionality that our implementation classes want to get from our API interfaces (in addition to regular access to all public elements). In this particular case it is the access to constructor via the newItem and listener via addChangeListener methods. Please note that these methods are abstract, without implementation and that this internal API is waiting for someone to register the implementation by calling the setDefault static method.

Who can provide implementation of this class? Well, only someone who can call the package private methods from the Item class. Does a class like that exist? Indeed, it does, any class in the same package as Item can do that. So just write:

does not exists: design.less.friend.AccessorImpl

Now we have the public API class Item with hooks for friends, we have the API for friends in the Accessor class and we also have a provider of this friend API. Now we need to bind this all together. As soon as the Item class is loaded, we need to register the implementation. In Java this can be done with the use of static initializer block in the API class:

does not exists: design.less.friend.Item.static

And this is all (especially if we want as clueless understanding as possible). We have just bind two Java packages together and provided an API that allows them to perform a privileged communication. As such any code in the same package as the Accessor class can call:

does not exists: design.less.friend.use

Because all the methods in the Accessor class are protected, only code in this one package can make the calls, no code in other packages has any chance to call these methods. As such the solution is safe and creates a secure teleport between the API package and the implementation one.

buy