In other OO languages like C++ or Smalltalk, designing a class is a single effort. There are some differences:
In C++, variables are typed to the class of the object that they can hold. That means that you end up with a lot of abstract classes that define interfaces, and then typing your variables to match these abstract classes.
In Smalltalk, variables are untyped (or dynamically typed). A programmer ends up relying on the fact that "any object that responds to these N messages" will work.
In Java, there's a way to handle abstract implementation that's different from both previous approaches. We've found that in many cases you want to build the following:
KenAuer has used this idiom extensively in his Java version of the venerable HotDraw framework [1].
I can think of two specific uses of this pattern in the database broker layer for Java that I'm writing now. (See CrossingChasms) In one place we substitute a Proxy for an actual class where the instance of the class has not yet been loaded from the database. To make this work we need to define each class that is "Proxyizable" as having an Interface that corresponds to its public protocol. All variables in the program then need to be typed to the Interface rather than to the actual class - this makes it possible to then transparently substitute a Proxy at any time.
In another part of the framework, I specify the Interface that any object must correspond to to be persistent. We also provide an implementation of these methods in a default base class (Persistent Object), but if a programmer wants his class to be persistent AND descend from some other class than our Persistent Object class, they can simply state that it implements the Persistency interface, and reimplement the methods themselves. The framework is written to take advantage of this, and will not care one way or another.
In C++, one creates a "pure" abstract base class or "pure ABC" (an abstract class having nothing but pure virtual functions with no implementations). This pure ABC serves precisely the same function as a Java interface. Then another abstract base class is declared as a subclass of the pure abstract base. This subclass provides the "default" behavior and state that will be used by most concrete classes. In fact, having the ABC be derived from the pure ABC is *exactly* equivalent in Java to having an abstract class which declares itself to implement an interface.
The bottom line is that Java interfaces are no different from a pure ABC in C++. Java makes the interface concept explicit (which is a boon to those who haven't already seen it before and/or haven't used them in this manner), but the way Java implements them is no different than using the C++ class construct strictly for inheritance of behavioral specification (and not for behavioral implementation nor for data/state).
Now - if Java had *full* polymorphism (like Smalltalk) and it was possible to send a message to (invoke a method of) any object capable of servicing that message request, then one could use Java interfaces as method parameters without forcing the actual parameter to be an instance of a class (or subclass of a class) that is explicitly declared to implement the interface. As long as the object implemented all those methods, the formal declaration of satisfying the contract wouldn't be required. I believe this is how the "Signature" feature of GnuCpp works when you enable it with the '-fsignatures' command-line option. The java.lang.reflect package can be used to achieve this functionality, but it is considerably more effort.
Of course this "ability" would introduce some risks/concerns for security and type-safety, but it would also add a lot more flexibility and power. This is especially handy in cases where you dont have access to library source code and can't change a class just so it is declared to implement an interface that it already has all the methods for (which might be the case for simple mixin-style classes that have very few methods). See ExtendToImplementInterfaces for another way of solving this problem using Java. -- BradAppleton
Each implementation, in order to be instantiated, is then instantiated with a member of a given Type descendent in its "State" member. By convention (or with tools...<grin>), the Type descendent associated with a given Implementation provides an implementation of each of the private getter/setter pairs defined in the Implementation descendent and all its ancestors (let's not talk about the state member itself yet).
Now, the "type" hierarchy is separated from the "class" hierarchy. My experience has been that the classes (Implementation and Interface) are much easier to refactor, multiple inheritance (of Implementation) works, and many nitty gritty problems in a distributed environment become significantly easier to solve. -- TomStambaugh
The caveat with this is that it prevents a user from substituting a different class into the code. We found this when we wanted to implement our own Vector class, we had to create a new interface that replicated the Vector's interface and change all the references in our code to use the new class. Of course if we couldn't get to source code we couldn't make the change. (I suppose we might have tried fiddling with the classpath, I don't know if we could have done something there.)
If you introduce a new interface for an existing class, for which you don't have access to the source, then you need to ExtendToImplementInterfaces -- MartinFowler
It's really not that different from using .c and .h file pairs in C (and if you don't use the ".h" files it's fairly analogous to not using the interfaces as Martin mentions above).
Even on my one person projects, I find that after a refactoring or two, it just makes my life easier to keep the interface code separate from the implementation code (both for minimal rebuilding, and more adaptable frameworks). -- BradAppleton
I'm confused. This seems to be saying that I'd have to put an abstract class in between a class and each of its subclasses. Suppose A is an abstract class, with concrete subclasses C1 C2 C3. Then to subclass C1 with C11, I'd first have to insert another abstract class A11 to intervene between C1 and C11??? -- RonJeffries
It is more like this: you don't accept references to objects of concrete classes anyplace, you accept references to objects of abstract classes or just interfaces. Robert's approach is almost the polar opposite of ExtremeProgramming in this respect. He tries to minimize dependency with much upfront work so that software does not have to change much in the face of change. I'm sure minimizing recompilation is a goal as well. It occurs to me that much of this approach could be a side-effect of the fact that refactoring just plain costs so much more in C++.
For anyone who is interested in seeing some philosophical differences, RobertMartin often engages in running debate on comp.object with someone named ElliottCoates?. The issues are: does OOA exist, and should code contain objects which model things in the domain or just high behavioral abstractions. -- MichaelFeathers
However, when I've tried to fully exploit this with Java's interfaces, they fail to provide adequately for non-public clients, such as Builders and Facades. Upon re-examining what I was trying to do, I find that as a C++, I am trying to use Java's interfaces to replace C++'s templates, and obviously they fail miserably at that.
What Java needs is either templates, 'friends' or some type of an ImplementationInterface? similar to Smalltalk's full polymorphism, but where the calls are validated as existing at compile time. -- ArickAnderson
interface SomeThingable { final static SomeThingable NULL = new Null();This way, clients can use SomeThingable.NULL to get a NullObject. some clients can extend SomeThingable.Null to get the default behaviour. (For instance inner classes) but there it is still an interface so classes can still choose to implement them for themselves.xxx someMethod(xxx); xxx xxxx(xxx) // more methods
static class Null implements SomeThingable { xxx someMethod(xxx) { // default implementation probably do nothing } xxx xxxx(xxx) // more methods implementations. }
}
A benefit of BuildInterfaceImplementationPairs is that adding methods to the interface becomes easier as many classes extend the default implementation.
(maybe you should use extended interface to do that but then you end up with lots of places where you need the extended interface but you only have the base interface. This may get very complex).
public interface SomeThingable {
public final static SomeThingable NULL = new SomeThingable() { xxx someMethod(xxx); xxx xxxx(xxx) // more methods };} -- DarJian?
This page mirrored in JavaIdioms as of April 29, 2006