Skip to main content

Eclipse, COM ... nothing new...

Posted by herkules on February 26, 2010 at 6:22 AM PST

When reading http://www.eclipsezone.com/articles/what-is-iadaptable/ I got the impression that certain solutions always come back, are quite universal and independant of domain, language or environment.
The question here is about getting certain aspects of an object that are not statically declared in a yet typesafe manner. The Eclipse world came up with IAdaptable, as it seems.

public interface IAdaptable {
  Object getAdapter(Class clazz);
}

public class HashMap implements IAdaptable {
  public Object getAdapter(Class clazz) {
    if (clazz == java.util.List.class) {
      List list = new ArrayList(this.size());
      list.addAll(this.values());
      return list;
    }
    return null;
  }
// ...
}

To be used like that:

IAdaptable adaptable = new HashMap();
List list = (List)adaptable.getAdapter(java.util.List.class);

Well, this immediately brought back good-old COM, Microsofts Component Object Model back to my mind. I alway felt comfortable with COM because COM objects are just as plain C++ objects and thus very fast.
The basics are the same. Every object is just a IUnknown (which transforms into an IAdaptable)

[
  object,
  uuid(00000000-0000-0000-C000-000000000046)
]
interface IUnknown {
  [restricted]
  HRESULT _stdcall QueryInterface([in] GUID* rrid, [out] void** ppvObj);
  [restricted]
  unsigned long _stdcall AddRef();
  [restricted]
  unsigned long _stdcall Release();
}

For this is nothing useful, derived interfaces can be defined.

class IStandardMathFunctions : public IUnknown
{
  public:
  STDMETHOD(Add)(long, long, long*)
  STDMETHOD(Sub)(long, long, long*)
};

class IAdvancedMathFunctions : public IUnknown
{
  public:
  STDMETHOD(Fibonacci)(short, long*)
}

But despite static inheritance, every COM object can deliver all of its capabilities (which is, all the interfaces it implements) at runtime from the object using QueryInterface() which is pretty much the same approach as with IAdaptable.

Interesting enough, when I designed an abstraction layer for my pet project http://www.flyingguns.com describing the static 3D world, my solution was similar again.

public interface Feature
{
}

public interface SceneObject extends Disposable
{
  /**
   * Provide a certain feature if available.
   */
  <T extends Feature> T getFeature( Class<T> clazz );
}

public interface Movable extends Feature
{
  void set( Vector3f position, Quat4f orientation );
}

public interface Damageable extends Feature
{
  void setDamage( float damage );
  void destroy();
}

public interface Aircraft extends Feature
{
  SceneObject[] getEngines();

  void setRudder();
  void setAileron();
  void setElevator();
}

The approach has a lot of advantages.

  • 100% typesafe, no casts due to generics
  • features can be combined deliberately per object
  • features may even change over time
  • makes delegation easy, good for code reuse
  • no runtime overhead
  • small footprint: resources for unsused features don't need to be loaded e.g. no fire and smoke if nobody wants to damage an object.

Sample code looks like that:

SceneObject fokkerobject = scene.create( DefaultDatabaseSetup.FOKKER_DR1 );

Movable fokkermover = fokkerobject.getFeature( Movable.class );
fokkermover.set( new Vector3f( 0f, 700, 0f ), new Quat4f( 0,0,0,1 ) );

WeaponSystem fokkerweapons = fokkerobject.getFeature( WeaponSystem.class );
SustainedFire machinegun = fokkerweapons.getWeapons()[0].getFeature(SustainedFire.class);

machinegun.startFire();

SceneObject bullet = machinegun.createProjectile();
Movable bulletmove = bullet.getFeature( Movable.class );
bulletmove.set( machinegun.getNuzzlePosition(), new Quat4f( 0,0,0,1 ) );

Projectile hitter = bullet.getFeature( Projectile.class );
Projectile.Hit hit = hitter.findHit( 50 );
if( null != hit )
   System.out.println( hit.getTarget() + " at " + hit.getLocation() );

 

 

Related Topics >>

Comments

The Capability Pattern

 Sounds a lot like this - http://weblogs.java.net/blog/2008/08/11/capability-pattern-future-proof-your-apis

 

DCI Architecture

Good link, Tim!

As far as I understand the basic idea of the "ability" or "capability" pattern is asking an object to give you an implementation of an interface. To tell the object "hey you, object, gimme a Component" or "hey, object, gimme a WeaponSystem", or "hey object, gimme a java.util.List".

Let's call these things you're asking objects for "Methodless Roles". So you have a "WeaponSystem" methodless role, or a "Component" methodless role, or a "java.util.List" methodless role.

So you ask objects for implementations of methodless roles. And objects return you a "methodful role", and you use that "methodful role" (an implementation of WeaponSystem, for instance) to do bad things.

So you "decorate" your classes with methods that return you methodful roles from methodless roles.

This is what Jim O. Coplien and Trygve Reenskaug (inventor of the MVC paradigm) call the DCI Architecture.

And it seems it's "A New Vision of Object-Oriented Programming" (sic).

Cheers,
Antonio

NetBeans Lookup Library?

That was invented ten years ago with the NetBeans Lookup Library!

null?

In your example you return null if the feature is not supported, but then in the example usage you don't check for null. Wouldn't it be better to return a default no-op object when the interface is not supported? (saving the null check)

Beans#getInstanceOf(Object, Class)

I always wish this method had been implemented.  How it has sat there, neglected, for years, is really beyond me.

Couldn't agree more.

Couldn't agree more.