Sunday 22 February 2009

Item 20: Prefer class hierarchies to tagged classes

Quite a straightforward one, this item, and applicable to all object-oriented programming languages. This explains the very basic use case for polymorphism: not using a type field or tag in a class to mark it for special behaviour, but using a class hierarchy instead. The disadvantages of tagged classes are well known: brittleness in case of change, inability to change behaviour without changing the tagged class itself and cluttered code. On the other hand, class hierarchies can often reflect a natural hierarchy of types in the application domain, leading to a closer match between code and the problem domain it's describing.

I don't know if there's much more to say about this one. But it does bring me straight back to when I did my very first C++, reading the Deitel & Deitel book while at university. I'm pretty sure they used the exact same example, with a hierarchy of shapes, methods to calculate area and all. A very neat idea that's useful in many circumstances, but shouldn't be overused (as discussed in item 16).

Jan Stette

Wednesday 18 February 2009

Item 19: Use Interfaces only to define types

This item is a short one but I found it confusing, maybe because I am a java novice.

The interface defines a type for its clients. Nothing else should be used. The main topic is using interfaces with static final fields that define constants, a so called constant interface. It should not be used because it pollutes the client with implementation details. The constant interface infects the hierarchy of a client class and can never be removed in the future. It could break binary compatibility.

So if I have a class that uses an interface with constants and the class is not final, then I could not remove this interface even if I don't need it? Is this because an inherited class could use it and I don't want to break existing code. Or because I can't because it would break because of the compiled class files? The binary compatibility?

When constants are needed, then they should be defined in a class or interface that they are strongly related to. They either should be a enum type or defined in a concrete utility class that can't be instantiated. Does this mean that the enum or the utility class is really outside the interface? It seems to me that the enum class or utility class must be defined outside the interface and part of the public face of the package. It is a little confusing. It would be nice to have a complete example. Only an example of the utility class is provided.

Use static import facility introduced in 1.5 to include constants from an utility class without the qualifiers.

This item continues the advice from the previous items. But it seems to only concentrate on constants and how to handle them. Maybe this item could be called "How to define constants with interfaces" or something like it.

Timothy Wright

Saturday 14 February 2009

Item18: Prefer interfaces to abstract classes

For defining types that permits multiple implemetations you have two choices (with their pros):
  • interfaces: support multiple inheritance

  • abstract classes: can contain implementations
Usecases which prefer interfaces:
  • retrofitting existing classes (example of the JDK, Comparable Interface introduction)

  • defining mixins (mixin is not a form of specialization but is rather a means to collect functionality), e.g. again the Comparable Interface is a good example for that…

  • Construction of nonhierarchical type frameworks

  • Functionality enhancements via wrapper classes (see Item 16)
Yet interfaces can not contain method implementations (e.g. default implementation or assistance/helper methods for implemetors of this interface). However exactly in this case a abstract class (a skeleton implementation for the interface) comes into play ;-). By convention they have a common "Abstract" prefix in the classname (e.g. AbstractXYZ for Interface XYZ). The idea is to use (and reuse) the abstract class better as implementation helper for an interface rather than as a type definition. Weaving the abstract skeleton class into the concrete class can be done via inheritance of via delegation. Note however that there is no private inheritance statement in Java (C++ there IS private inheritance and it communicates the intention very obvious as implementation inheritance). Does anybody know why there is no private inheritance in Java ?

As abstract skeleton classes are ideal candidates for inheritance (refinement of the default helper implementation) we should document the class as described in Item 17.

One special case of a skeleton class is the "Simple" Implementation which is a non-abstract helper class with a common "Simple" prefix. However I am unsure if this is a common prefix or convention. Even in the JDK source I only found .SimpleEntry and .SimpleImmutableEntry as Simple inner implemetation classes. Does anybody know if this is a common convention and often used ? My impression is that AbstractXYZ means the skeleton approach is used much more often.

Usecases which prefer abstract classes (as type definition)
  • it is far easier to evolve and abstract class than an interface.
    You can simply add the new method with a default implementation and all classes which inherit from that class (and get recompiled ! ;-) will inherit that default implementation. Impossible with interfaces.

  • Workaround for interfaces would be to add the method declaration to the interface and a default method implementation to the skeleton class, however classes NOT inheriting from that skeleton class will break.
However the topic with evolving interfaces and abstract classes or better evolving API in general is much more complicated as the following article shows: (which IIRC mentioned also in another Item recently)

http://wiki.eclipse.org/index.php/Evolving_Java-based_APIs

http://wiki.eclipse.org/Evolving_Java-based_APIs_2

Josh also mentioned that changing an interface (once it is released) is nearly impossible. Well I think everybody, who has clients which are using your written software (programming against your API) has learned this is some way (maybe a painful) way. So get it right in the first place. But how to do this ? I recently read a book "Framework Design Guidelines" (okay from the other side, M$ ;-) but some tips about "how to create and especially test with Clients how good and understandable it the API" are really worth to read IMO.

http://www.amazon.com/Framework-Design-Guidelines-Conventions-Development/dp/0321246756

Bernhard Merkle