For example, if we have an interface Point with two methods int x()
and int y(), then we can expose a static method Point.of(int x, int y)
that produces a (hidden) implementation of the interface.
1) Be aware, since Java 8 you can add a static method in an interface, but before you cannot.
2) I have some difficulties to understand this advise :
So, if x and y are both zero, we can return a special implementation
class PointOrigoImpl (with no x or y fields), or else we return
another class PointImpl that holds the given x and y values. Ensure
that the implementation classes are in another package that are
clearly not a part of the API (e.g. put the Point interface in
com.company. product.shape and the implementations in
com.company.product.internal.shape).
Here is an example Point class that matches to the text you refer to:
package com.company.product.shape;
import com.company.product.internal.shape.PointOrigoImpl;
import com.company.product.internal.shape.PointImpl;
public interface Point{
int x();
int y();
static Point of(int x, int y) {
if (x == 0 && y == 0){
return new PointOrigoImpl();
}
return new PointImpl(x, y);
}
}
If the Point interface is provided to clients and that it declares the factory method, to compile fine, the Pointinterface has to necessarily have the visibility to PointOrigoImpl and PointImpl classes in order to instantiate them.
As the Point interface and the subclasses are in two distinct packages (com.company.product.shape and com.company.product.internal.shape),
that means that PointImpl and PointOrigoImpl should have a public constructor otherwise Point could not instantiate them.
Finally, clients who manipulate Points may also instantiate the constructor of PointImpl and PointOrigoImpl subclasses.
It defeats the purpose of a factory that doesn't want to expose the implementation to choose itself the implementation to return.
An interface is public and is designed to be fully accessible to the client.
So as a general rule it should not contain implementations that we don't want to expose to clients.
3) Here :
The advantage being that this enforces encapsulation? However, if the
implementation is package private and not part of the API, would that
not create access problems? If the client, "world" as here:
doesn't have access to the implementation of Point then how can it
possibly instantiate a new Point as above for "do this"?
I suppose you wonder why the text says :
Ensure that the implementation classes are in another package that are clearly not a part of the API.
In the text you refer to, the author wants to let the interface be known by Point clients but he doesn't want to subclasses of Point be known because he wants to decide the Point subclass to return according to the parameter used in the factory Point.
Point clients will know only this interface.
In Java, in order to not providing the access to implementation classes you can use public class, interface and inner private class :
- the public class is exposed to clients to call the factory method that returns the implementation
- the interface class is known by all and has no coupling with the implementation.
- The inner private class makes part of the public class. The client cannot sees it but the factory see it to return the suitable implementation.
With the example you refer to, it would be straight.
Point (interface) :
public interface Point{
int x();
int y();
}
Point factory (public class that contains private classes) :
public final class PointFactory {
// private classes
private class PointImpl implements Point {
private int x;
private int y;
private PointImpl(int x, int y) {
this.x = x;
this.y = y;
}
public int x() {
return x;
}
public int y() {
return y;
}
}
private class PointOrigoImpl implements Point {
public int x() {
return 0;
}
public int y() {
return 0;
}
}
// end private classes
public Point of(int x, int y) {
if (x == 0 && y == 0) {
return new PointOrigoImpl();
}
return new PointImpl(x, y);
}
}