Showing posts with label interfaces. Show all posts
Showing posts with label interfaces. Show all posts

16 Nov 2008

Item 52: Refer to objects by their interfaces

(A side related note first: I still need to get the 2nd edition of Effective Java. The 1st edition copy I have is very well-read, though! Highly recommended for all Java programmers. Edit: I did buy the book in the end; InformIT sells non-DRM PDF copies, so yay!)

Anyway, this is a short rant about one of my personal peeves. To put it succinctly, I'll borrow a line out of Effective Java: Item 52: Refer to objects by their interfaces. I believe this so strongly that I've used that as the post title too. :-)

Let's motivate with an example:


Vector<Number> getPrimes(Number from, Number to);
Collection<Number> getPrimes(Number from, Number to);

Which one of the two would you prefer for your interface? Without reservation, I'd prefer the second one, because it doesn't name a concrete return type, thus giving the implementation more flexibility; for example, instead of having to create a Vector, you could use sets, singletons (from Collections), arrays (with a List view created by Arrays.asList), or even a custom type, as long as it fulfils the Collection interface. Clients that want to use Vector could simply construct one from the returned collection.

The same applies to incoming parameters in an interface too. For example:


Number sum(Vector<Number> numbers);
Number sum(Collection<Number> numbers);

Which one do you prefer here? (It could be argued that List should be preferred to Collection if the order of the numbers is significant; however in this example of summing, addition is commutative so this doesn't matter.) To use the first signature would put a burden on your interface's clients; there is no easy construction syntax for making a Vector, whereas on the other hand there are easy ways to express creation of lists (Arrays.asList is variadic), singletons, etc.

In summary, where there are interface types you can use for incoming parameters and return values, you should definitely prefer them. Concrete types are for instantiation only, or perhaps for use in legacy hierarchies that don't have interface types. (For instance, I consider it totally broken that Reader and Writer are abstract classes instead of interfaces.)

14 Oct 2008

More about interfaces than you can throw a HissyFitException at

This post is in response to Mike Stone's Please Hold on the Interfaces.

I believe in judicious use of interfaces. Sometimes it's warranted, such as when it represents concepts that lend themselves to multiple unrelated implementations (e.g., Runnable or Callable).

This is doubly useful in, say, (pre-3.0) GNU C++, where you can cast references of any type to a given interface (or signature as G++ calls it) as long as all members of the interface are implemented. It's much, much more flexible than interfaces as seen in Java or C# (where you have to declare upfront in the implementing class that it's in fact implementing some interface).

Where all implementations are likely to have common functionality, having an abstract class is more sensible, in my opinion. Sometimes (such as in the Java collections framework), having both is sensible, too.


Interfaces do use vtables (in fact all interface method invocations are virtual), so whoever says using interfaces is faster because it avoids virtual calls has never profiled their code before, and worse, has no concept of how high-level source code translates to low-level object code. (Yes, I just downvoted their answer.)

I never see a reason to avoid virtual functions when polymorphism is warranted. Seasoned C++ programmers will tell you that what slows your program down with using virtual functions isn't the indirect function call, but the diminished opportunity for inlining.

However, with a managed platform like Java or .NET, the JIT can easily inline for the common case (profile-guided optimisation for the win!), and deoptimise when the common case is no longer the common case. (For lurkers and archives, what I mean is that if a virtual method is being called mostly through instances of one specific concrete class, that concrete implementation can be inlined into the JITted code until other concrete classes start being used more frequently—in which case the code is re-JITted appropriately.)

In contrast, programmers who think they know where their program's bottlenecks are, without profiling for the common cases, are performing ignorance-guided optimisation.


I don't agree with the "call protected virtual initialisation method from constructor" approach though. Remember that in the superclass constructor, the subclass portion of the object isn't constructed yet, and any such virtual methods you call had better make sure that they only access the superclass portion of the object.

In C++, this is enforced—this is a feature, not a bug. Virtual functions do not exhibit virtual behaviour when called (directly or indirectly) from constructors and destructors. I have some code to demonstrate this:


#include <iostream>

class Base {
public:
    Base() {hw();}
    virtual ~Base() {gw();}

    virtual void println(char const* line) = 0;
    void hw() {println("Hello, world!");}
    void gw() {println("Goodbye, world!");}
};

class Derived : public Base {
public:
    Derived() {}
    virtual ~Derived() {}

    virtual void println(char const* line) {
        std::cout << line << '\n';
    }
};

int
main()
{
    Derived d;
    return 0;
}

Here, even though we're instantiating a Derived object, where the println function is defined, while inside the Base constructor (and destructor) Derived::println is not being used at all.

If the constructor/destructor calls println directly, that will generate a compiler error (since a pure virtual function is being called). In this indirect case, the compilation will go fine, but because Base's vtable is used until execution reaches Derived's constructor, the println() call inside hw() will not work as (naively) expected.

Like I said, this is a feature, not a bug. While the price you pay is that you cannot use virtual behaviour inside constructors and destructors, this actually makes your program safer, by not requiring your virtual functions to be specially coded to only use members in the base class.

So, back to the use of a "protected virtual method" to allow mockable initialisation. The proper way to do this, in my view, is to make an initialize() method, that is called by client code after the object is constructed. Since initialize() isn't being called by the constructor, it means that the object is fully constructed, and that it's safe to call into the appropriate virtual method to do the appropriate type of database initialisation (or whatever).