• Share this article:

Adapters, part deux

Tuesday, October 23, 2007 - 23:37 by Wayne Beaton

In my last post on the topic, I hinted that adapters can be used to loosen the coupling in your code and then showed how you could use adapters to add even more coupling in your code. Originally, I had a domain class, Person, that knew how to provide information to the Properties view by virtue of implementing the IPropertySource interface. Then, I “decoupled” the domain class from the interface by instead implementing a getAdapter method that returned an instance of a different class when asked for an adapter of the type IPropertySource. I put “decoupling” in quotes, because this step really does nothing to decrease coupling; all it does is move the code somewhere else, the Person class is still just as tightly coupled to the IPropertySource interface. In fact, it is now also coupled to the IAdaptable interface.

Take a few minutes to familiarize yourself with the previous steps. They’re here and here. I’ll wait…

The next step is to completely decouple the domain class from IPropertySource. To do this, I can change the getAdapter method:

public class Person implements IAdaptable {
	private String name;
	private Object street;
	private Object city;

	public Person(String name) {
		this.name = name;
		this.street = "";
		this.city = "";
	}

	public Object getAdapter(Class adapter) {
		return AdapterManager.getDefault().getAdapter(this, adapter);
	}
	...
}

Previously, the getAdapter method checked the type of the desired adapter and created an appropriate instance (if possible) itself. Now, the method makes a call to the AdapterManager, which can take care of figuring out how to adapt the instance.

For this to work, the adapter manager needs to be told how to adapt the type. This can be done declaratively through the plugin.xml file:

<plugin>
   <extension
         point="org.eclipse.core.runtime.adapters">
      <factory
            adaptableType="org.eclipse.example.Person"
            class="org.eclipse.example.adapters.PersonPropertiesSourceAdapterFactory">
         <adapter
               type="org.eclipse.ui.views.properties.IPropertySource">
         </adapter>
      </factory>
   </extension>
</plugin>

This extension defines an adapter for instances of the org.eclipse.example.Person class. When asked to adapt to the org.eclipse.ui.views.properties.IPropertySource, the PersonPropertiesSourceAdapterFactory should be used. This factory class is defined as such:

public class PersonPropertiesSourceAdapterFactory implements IAdapterFactory {
	public Object getAdapter(Object adaptableObject, Class adapterType) {
		if (adapterType == IPropertySource.class)
			return new PersonPropertySource((Person)adaptableObject);
		return null;
	}

	public Class[] getAdapterList() {
		return new Class[] {IPropertySource.class};
	}
}

The getAdapter method does the heavy lifting and creates the adapter. The getAdapterList returns a list of the types of adapters the factory can create.

Adapter factories can also be registered programmatically using APIs on the AdapterManager class.

With the adapters all safely registered, we can make one more change to our code. When adapters are used, they are usually used in three steps::

  1. If the object implements the required interface, use the object
  2. If the object implements IAdaptable, call the getAdapter method an use the returned object; if something other than null is answered, use the returned value
  3. Get the AdapterManager to try and adapt the object

The final step actually makes the current implementation of getAdapter in the Person class redundant. We can simply remove it, and remove the reference to the IAdaptable interface from the class. That is, the Person class simplifies to:

public class Person {
	private String name;
	private Object street;
	private Object city;

	public Person(String name) {
		this.name = name;
		this.street = "";
		this.city = "";
	}
	...
}

Voila! the domain class is totally decoupled from the adapter type. Instances of the person class, when selected in your favourite view will populate the Properties view.

There’s still a couple of issues to deal with. But this entry’s getting a little long, so we’ll talk about them next time (and I’ll post some example code).