From the view of an ALE client it is preferable to share a common view on a reader, without having to care about vendor versions, manufacturer and so on. This chapter introduces the concept of logical, composite and base readers that shall provide a uniform access to readers of different types.
In a first step the chosen design pattern are presented. Then the basic implementation of the Logical Reader API is shown and explained more specific.
The documentation for the usage of the Logical Reader API through the LogicalReaderManager is not covered here. Please refer to the chapter "LogicalReader Management"
In the EPC Standard basically two reader types are defined (according to chapter 10.2):
The terminology of a base reader and a composite reader was used directly in a composite pattern. The composite pattern gives a clean and easy way how to treat objects and compositions of objects in the same way. The direct application on the logical reader lead to the design shown in the next image.
To create a reader a static factory method inside LogicalReader is used. Each Reader has to provide two methods. These are a Default-Constructor with the same name as the Reader and an initialize method "public void initialize(String name, LRSpec spec)". The factory-method extracts the reader type from the LRSpec. Through reflection and dynamic class-loading a new instance of this type is created. That for the default-constructor is used. Once this new instance is available the factory calls the initialize-method on the new object. This makes possible that a reader can prepare itself but without the need of complicated constructor-search in the object-creation process.
The following subsections "LogicalReader", "CompositeReader" and "BaseReader" will explain in more detail the different building blocks of the composite pattern.
In all the diagrams the following conventions were used.
To simplify the Tag propagation inside the Logical Reader API an Observer Pattern was chosen. There are two cases to consider, first when an EventCycle acts as an Observer and second when a CompositeReader uses the ObserverPattern for its children. However these cases are conceptually equal as will be explained shortly.
As the image visualizes a LogicalReader acts in any case only as Observable where CompositeReaders or EventCycles can register. CompositeReaders register themself on other LogicalReaders, therefor act as Observer and as Observable in the same time.
The following image shall give an example.
Whenever a BaseReader reads a Tag it calls its notify-method. This method calls the update-method on all the Observers. Like that one BaseReader can send one Tag to multiple receivers. In the same manner CompositeReader calls its notify method whenever a component triggered the update-method.
There are many different physical readers available and one might be interested in using them all in one application without the need to implement several BaseReaders. Therefor the adaptor pattern was used to generalize the notion of a BaseReader.
The Adaptor pattern translates an interface of a class into another interface. In this case the interfaces of different readers are translated into the interface BaseReader.
One drawback of this pattern is, that for each new reader one has to implement a new adaptor. Nevertheless, in our opinion the advantages still overrule the drawbacks.
The instantiation of such adaptors are explained in the section BaseReader.
As motivated by the CompositePattern LogicalReader declares the interface to a reader. In following the methods contained in LogicalReader are explained shortly.
The first case is simply the instantiation of a new CompositeReader object through a constructor-call. The setup of a CompositeReader is explained in the section about CompositeReader.
The second case is more complicated an shall be explained in the section about BaseReaders.
In both of the two cases a LogicalReader is returned.
The class CompositeReader models a composite in the composite pattern that can be composed by different other components. The components are in this case LogicalReaders.
The image above shows only new or overwritten methods from LogicalReader.
In the initialize-method the leafs of the CompositeReader are aquired from the LogicalReaderManager by calling the Logical Reader API. The CompositeReader registers itself on the leafs as an Observer.
BaseReader models the leaf of the composite pattern. BaseReader provides more methods that can be used to communicate directly with a physical reader.
In the image above only new or overwritten methods from LogicalReader are shown.
BaseReader is still an abstract class and cannot be instantiated by the Logical Reader API. In following the instantiation of reader adaptors through the createReader factory-method will be explained.
From the LRSpec the fully qualified reader type is extracted as a string. This type has to be provided through an LRProperty. Through dynamic class loading the class is loaded and a new instance of the adaptor is created. This instance is already of type LogicalReader. The subsequent call to the initialize-method on the new LogicalReader calls (through the mechanism of overloading) the correct initializer method.
For further information on the existing adaptors and how you can write your own adaptor refer to the chapter "Implement an Adaptor".