I'm reading Bruce Eckel's Thinking in Java and there's an exercise I'm just not getting:
Pg. 161: Exercise 8: (4) Following the form of the example Lunch.java, create a class called ConnectionManager that manages a fixed array of Connection objects. The client programmer must not be able to explicitly create Connection objects, but can only get them via a static method in ConnectionManager. When the ConnectionManager runs out of objects, it returns a null reference. Test the classes in main( ).
I came up with the following solution:
// TestConnection.java
import java.util.*;
public class TestConnections {
public static void main( String[] args ) {
Connection cn = Connection.makeConnection();
for (int i = 0; i != 6; ++i) {
Connection tmp = ConnectionManager.newConnectiton();
if ( tmp == null )
System.out.println("Out of Connection objects");
else {
System.out.println("Got object: " + tmp );
}
}
}
}
And a second file in the same directory meaning everything ends up in the same default package:
// ConnectionManager.java
class Connection {
private Connection() {}
static Connection makeConnection() {
return new Connection();
}
}
public class ConnectionManager {
static private Connection[] _connections = new Connection[5];
private ConnectionManager() {}
static public Connection newConnectiton() {
for ( int i = 0; i != _connections.length; ++i ) {
if ( _connections[i] == null ) {
_connections[i] = Connection.makeConnection();
return _connections[i];
}
}
return null;
}
}
The thing is that the client program can directly create Connection objects via the static Connection.makeConnection factory, which seems to violate the exercises goals. Yet if I make the ConnectionManager.java a separate package then import it complains that it can't find a definition for Connection.
I feel like something is going over my head here, but I'm not sure what.
Here is the code for Lunch.java which is referenced in the question:
//: access/Lunch.java
// Demonstrates class access specifiers. Make a class
// effectively private with private constructors:
class Soup1 {
private Soup1() {}
// (1) Allow creation via static method:
public static Soup1 makeSoup() {
return new Soup1();
}
}
class Soup2 {
private Soup2() {}
// (2) Create a static object and return a reference
// upon request.(The "Singleton" pattern):
private static Soup2 ps1 = new Soup2();
public static Soup2 access() {
return ps1;
}
public void f() {}
}
// Only one public class allowed per file:
public class Lunch {
void testPrivate() {
// Can't do this! Private constructor:
//! Soup1 soup = new Soup1();
}
void testStatic() {
Soup1 soup = Soup1.makeSoup();
}
void testSingleton() {
Soup2.access().f();
}
} ///:~
Each class has a scope. In your code, the Connection class has package scope (i.e., it can only be accessed by classes within that same package). This means that moving the ConnectionManager class to another package moves it outside the scope that can see the Connection class; that's the failure you see. Those connections have to be co-located with their factory.
In reality, what you would actually do is you create a public interface that defines what operations on the Connection are, and a private implementation of that interface inside the ConnectionManager; the newConnection method can then just declare that it returns an instance of the interface, and the package protection mechanism stops anyone from prying behind that interface (well, not without using the funkier parts of reflection).
The trick: Connection shouldn't be a concrete class but an interface. A second class (ConnectionImpl) provides an implementation of this interface.
Only the ConnectionManager can instantiate instances of this concrete class and returns the interface.
Example (reduced to show access modifiers):
public interface Connection() {
}
public class ConnectionManager {
private static class ConnectionImpl implement Connection {
private ConnectionImpl() {
}
}
public static Connection createConnection() {
return new ConnectionImpl();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With