On Fri, 27 Sep 2013 18:09:13 -0300, Barry Books <[email protected]> wrote:

It useful for library modules because you can build a set of pages,
components and classes that work on Interfaces instead of concrete classes.
The application can just implement the interfaces and contribute them. In
my case about I have an Invoice interface that's is defined in a shop
module. The shop module just operates on the Interface. Then I have a web
app that implements that Interface in my case as a DynamoDB object. Others could implement it as a JPA or Hibernate object.

A library module can define interfaces and @Inject them without the module itself defining the service implementations. Of course, to test or use it, you'll need to create and define implementations for that services, maybe mocks in some module class outside the library component that defines that inteface, but that's not a requirement for building a module library. All you need to do is to *not* define the service and let other module class (maybe AppModule) do that. If no one actually provides a service implementation, Tapestry will raise an exception explaining that.

There are several reasons it needs to be in the core.

1. It would be great if BeanEditForm used it so  BeanEditForm could work
with interfaces.

Actually, implementing BeanModel yourself or decorating one created through BeanModelSource and passing it to BeanEditForm and BeanEditor, probably by decorating or advising the BeanModelSource service, you can implement the BeanModel.newInstance() method (or decorate the BeanModel created by BeanModelSource and override the newInstance() method) and have it instantiate any object you want automatically (as long at it implements the interface, of course).

2. If you are going to build a Modules based on Interfaces it needs to be
in the core otherwise everyone will end up with a different implementation and they don't interoperate.

I'm sorry, I'm not following you here, maybe because of my explanation below.

3. It's easier to use in pages/components than locator.

Agreed. If it's just about instantiating objects based on interfaces, I've just discovered ObjectProvider and MasterObjectProvider (which is basically a façade around the list of ObjectProviders), which I believe may fit the scenario you're thinking:

/**
* Object providers represent an alternate way to locate an object provided somewhere in the {@link * org.apache.tapestry5.ioc.Registry}. Instead of using a just the service id to gain access to a service within the * Registry, object providers in different flavors are capable of vending, or even creating, objects of disparate types
 * from disparate sources.
 * <p/>
* Object providers are consulted in a strict order, and the first non-null result is taken.
 * <p/>
* In many cases, an object provider searches for additional annotations on the element (usually a parameter, or perhaps
 * a field) for which a value is required.
 */
public interface ObjectProvider {
<T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator);
}

4. Tapestry is really good about using Interfaces to define functionality
but it's difficult to do this in modules that define components. This
solves most of the problems.

I'm sorry, I'm not following you here, maybe because of my explanation above.

By the way, for many scenarios, I've found that defining a chain of responsibility service (which Tapestry calls chain of command for some reason) the best way for dealing with stuff with distributed configuration instead of just defining a service. For example, your scenario deals with instantiating objects defining a given interface the library which defines this interface doesn't know the implementations. So I could define an InterfaceInstantiator service:

@UsesOrderedConfiguration(InterfaceInstantiator.class);
public interface InterfaceInstantiator<T> {
        /**
* Instantiates an object of the given type. If this implementation doesn't support that type,
         * this method should return null.
         */
        <T> instantiate(Class<T> type);
}

And define it as a service that receives instances of itself as its distributed configuration (!!!):

public static InterfaceInstantiator buildInterfaceInstantiator(
        OrderedConfiguration<InterfaceInstantiator> configuration,
        ChainBuilder chainBuilder) {

        return chainBuilder.build(IterfaceInstantiator.class, configuration);

}

By the way, many of the most important Tapestry services are defined in this Inception-esque this way (MasterDispatcher, ComponentClassTransformerWorker, DataTypeAnalyzer, InjctionProvider).

Summary: what I wrote here will probably be turned into a blog post about chain of responsibility in Tapestry-IoC later, but I think ObjectProvider and MasterObjectProvider are widely unknown interfaces that already do what you want.

--
Thiago H. de Paula Figueiredo

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to