When you need to query the services, you often end up with code that loops over all available instances. For example, when you want to create a layer for a model, you would request all layer factories from the services and loop over them until you encounter a layer factory that accepts the model.

To prevent such boilerplate code, the Lucy API comes with composite implementations for all interfaces, available as services. To learn more about composite patterns, see the web page The composite pattern.

The available services documentation contains an overview of the available composite implementations.

For this example, we use the TLcyCompositeGXYLayerFactory, which is a composite implementation of ILcdGXYLayerFactory. As you can see in the Javadoc of TLcyCompositeGXYLayerFactory, two constructors are available: the default constructor and a constructor with an ILcyLucyEnv parameter.

The default constructor creates a regular composite implementation of ILcdGXYLayerFactory, as explained in The composite pattern.

On the other hand, the constructor with the ILcyLucyEnv parameter works on the registered service objects of the Lucy back-end. You can add or remove an ILcdGXYLayerFactory instance to or from this composite instance through the services:

Program: Adding an ILcdGXYLayerFactory directly as service object on the back-end
  ILcdGXYLayerFactory firstLayerFactory;
  ILcyLucyEnv aLucyEnv;
  aLucyEnv.addService(firstLayerFactory);

or through a new instance of TLcyCompositeGXYLayerFactory:

Program: Adding an ILcdGXYLayerFactory as service object trough a composite implementation
  ILcdGXYLayerFactory secondLayerFactory;
  ILcyLucyEnv aLucyEnv;
  new TLcyCompositeGXYLayerFactory(aLucyEnv).addGXYLayerFactory(secondLayerFactory);

Because the TLcyCompositeGXYLayerFactory works directly on the Lucy services, it has no internal state and you can create a new instance whenever you need one, for example:

Program: Adding multiple instances on a composite implementation
  ILcdGXYLayerFactory thirdLayerFactory, fourthLayerFactory;
  new TLcyCompositeGXYLayerFactory(aLucyEnv).addGXYLayerFactory(thirdLayerFactory);
  new TLcyCompositeGXYLayerFactory(aLucyEnv).addGXYLayerFactory(fourthLayerFactory);//we may create a new TLcyCompositeGXYLayerFactory, or re-use the existing one

The code snippets above will result in four ILcdGXYLayerFactory instances registered as services with the Lucy back-end:

Program: Retrieving the number of registered instances
  ILcyLucyEnv aLucyEnv;
  aLucyEnv.getServices(ILcdGXYLayerFactory.class).size();//this will return 4
  new TLcyCompositeGXYLayerFactory(aLucyEnv).getGXYLayerFactoryCount();//this will also return 4

Warning: although the composite implementations and the services behave very similarly, there are two possible pitfalls when you are using them both:

  1. If you add an object A through the Lucy services — ILcyLucyEnv.addService(A) — , you can only remove it through the services, and not through a composite implementation — ILcyLucyEnv.removeService(A)).

    This restriction also applies to the composite implementations. Objects you add to the composite implementation can only be removed through a back-end-based composite implementation. and not directly through the services.

    This also applies to the ALcyFormat instances. When using a TLcyFormatTool to plug in an ALcyFormat, you should also use the TLcyFormatTool to unplug the ALcyFormat.

  2. Adding an object through the Lucy services — ILcyLucyEnv.addService(Object) — registers the object for every interface or class it implements or extends, while in the composite implementation it is only registered for the specified class. Let us go back to the example of the TLcyCompositeGXYLayerFactory. Suppose we have a ILcdGXYLayerFactory that also implements ILcdGXYLayerDecoder and ILcdGXYLayerEncoder. Registering it directly with the services leads to:

    Program: Registering an object which implements multiple interfaces directly to the services
        ILcyLucyEnv aLucyEnv;
        ILcdGXYLayerFactory layerFactory;//suppose it also implements ILcdGXYLayerDecoder, ILcdGXYLayerEncoder
        aLucyEnv.addService(layerFactory);
        aLucyEnv.getServices(ILcdGXYLayerFactory.class);//returns a list containing layerFactory
        aLucyEnv.getServices(ILcdGXYLayerDecoder.class);//returns a list containing layerFactory
        aLucyEnv.getServices(ILcdGXYLayerEncoder.class);//returns a list containing layerFactory

    Performing the same operation on the TLcyCompositeGXYLayerFactory only registers the ILcdGXYLayerFactory as layer factory, and not as ILcdGXYLayerDecoder or ILcdGXYLayerEncoder:

    Program: Registering an object which implements multiple interfaces through a composite implementation
        ILcyLucyEnv aLucyEnv;
        ILcdGXYLayerFactory layerFactory;//suppose it also implements ILcdGXYLayerDecoder, ILcdGXYLayerEncoder
        TLcyCompositeGXYLayerFactory composite = new TLcyCompositeGXYLayerFactory(aLucyEnv);
        composite.addGXYLayerFactory(layerFactory);
        aLucyEnv.getServices(ILcdGXYLayerFactory.class);//returns a list containing layerFactory
        aLucyEnv.getServices(ILcdGXYLayerDecoder.class);//returns a list which does NOT contain layerFactory
        aLucyEnv.getServices(ILcdGXYLayerEncoder.class);//returns a list which does NOT contain layerFactory