Generic Factory Service

Introduction

The Consultingwerk.Framework.IFactory service provides a configurable method of constructing object instances. The factory allows a caller to request a new object instance based on a (contract) type, an optional type alias and if required an array of constructor parameters.

The default implementation Consultingwerk.Framework.Factory implements as well the Consultingwerk.Framework.FactoryRegistry interface.

It's recommended to launch the default factory with an services.xml file entry like this:

<ttServiceLoaderRow>
  <Order>0</Order>
  <ServiceTypeName>Consultingwerk.Framework.IFactory,Consultingwerk.Framework.IFactoryRegistry</ServiceTypeName>
  <ServiceClassName>Consultingwerk.Framework.Factory</ServiceClassName>
</ttServiceLoaderRow>

Using the Factory Registry

The factory registry provides the following methods:

    /**
     * Purpose: Registers object factories from the given file
     * Notes:
     * @param pcFileName The file naem to load factories from
     */
    METHOD PUBLIC VOID LoadFromFile (pcFileName AS CHARACTER).

    /**
     * Purpose: Registers an object factory type
     * Notes:
     * @param poType The type to register
     * @param poImplementation The implementation of the type
     */
    METHOD PUBLIC VOID RegisterFactory (poType AS Progress.Lang.Class,
                                        poImplementation AS Progress.Lang.Class).

    /**
     * Purpose: Registers an object factory type
     * Notes:
     * @param poType The type to register
     * @param pcAlias The type alias
     * @param poImplementation The implementation of the type
     */
    METHOD PUBLIC VOID RegisterFactory (poType AS Progress.Lang.Class,
                                        pcAlias AS CHARACTER,
                                        poImplementation AS Progress.Lang.Class).

    /**
     * Purpose: Registers an object factory type
     * Notes:
     * @param poType The type to register
     */
    METHOD PUBLIC VOID UnregisterFactory (poType AS Progress.Lang.Class).

    /**
     * Purpose: Registers an object factory type
     * Notes:
     * @param poType The type to register
     * @param pcAlias The type alias
     */
    METHOD PUBLIC VOID UnregisterFactory (poType AS Progress.Lang.Class,
                                          pcAlias AS CHARACTER).

The RegisterFactory method allows to regsiter factories by mapping an (contract) type to the type of an implementation. Optionally type aliases can be used if the same factory type needs to be implemented differently in different use-cases.

oFactory = {Consultingwerk/get-service.i IFactory} .
 
oFactory:RegisterFactory (GET-CLASS (IFetchDataRequest),
                          GET-CLASS (FetchDataRequest)) .

Using an factory.xml filie

Alternativly, factories can be configured using an xml file. These xml files can be loaded using the LoadFromFile method of the IFactoryRegistry interface.

<?xml version="1.0"?>
<ttFactoryLoader xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ttFactoryLoaderRow>
    <FactoryType>Consultingwerk.OERA.IFetchDataRequest</FactoryType>
    <FactoryAlias/>
    <InstanceType>Consultingwerk.OERA.FetchDataRequest</InstanceType>
  </ttFactoryLoaderRow>
</ttFactoryLoader>

Using the JSON configuration file

The JSON Configuration File Format supports the configuration of multiple factory.xml files using the loadFactories section. This section should be a JSON string array of xml file names.

{
    "basedOn": "",
    "inheritStaticProperties": false,
    "staticProperties": {},
    "applicationSettings": ".restapplicationsettings",
    "inheritLoadServices": false,
    "loadServices": [
        "Consultingwerk/Framework/Server/common_services.xml",
        "Consultingwerk/SmartFramework/services_server.xml"],
    "loadFactories": [
        "Consultingwerk/Framework/factory.xml"],
    "inheritManagers": false,
    "managers": {
        "Ccs.Common.IServiceManager": "Consultingwerk.Framework.CcsServiceManager"},
    "inheritCustomLogEntries": false,
    "customLogEntries": [],
    "inheritAliases": false,
    "aliases": null,
    "sessionExport":  null
}

Using the Factory

The IFactory interface provides the follwoing methods to request a new object instance to be created by the IFactory implementation.

    /**
     * Purpose: Creates an object instance
     * Notes:   Factory method for object instances referenced by the provided
     *          type (typically an interface)
     * @param poType The type to create an instance of
     * @return The created object instance
     */
    METHOD PUBLIC Progress.Lang.Object CreateInstance (poType AS Progress.Lang.Class).

    /**
     * Purpose: Creates an object instance
     * Notes:   Factory method for object instances referenced by the provided
     *          type (typically an interface)
     * @param poType The type to create an instance of
     * @param pcAlias The alias of the object
     * @return The created object instance
     */
    METHOD PUBLIC Progress.Lang.Object CreateInstance (poType AS Progress.Lang.Class,
                                                       pcAlias AS CHARACTER).

    /**
     * Purpose: Creates an object instance
     * Notes:   Factory method for object instances referenced by the provided
     *          type (typically an interface)
     * @param poType The type to create an instance of
     * @param poParameters An array of parameters to pass to the object's constructor, Holder types will be passed as their Value to the constructor
     * @return The created object instance
     */
    METHOD PUBLIC Progress.Lang.Object CreateInstance (poType AS Progress.Lang.Class,
                                                       poParameters AS Progress.Lang.Object EXTENT).

    /**
     * Purpose: Creates an object instance
     * Notes:   Factory method for object instances referenced by the provided
     *          type (typically an interface)
     * @param poType The type to create an instance of
     * @param pcAlias The alias of the object
     * @param poParameters An array of parameters to pass to the object's constructor, Holder types will be passed as their Value to the constructor
     * @return The created object instance
     */
    METHOD PUBLIC Progress.Lang.Object CreateInstance (poType AS Progress.Lang.Class,
                                                       pcAlias AS CHARACTER,
                                                       poParameters AS Progress.Lang.Object EXTENT).

The CreateInstance method requires a (contract) type and optionally a type alias and/or parameters for the constructor of the implementation.

Creating an object without constructor parameters

To create an object with no constructor parameters, the CreateInstance method is called with only the type argument. The type needs to be registered previously with the IFactory service.

oCharacterHolder = CAST (oFactory:CreateInstance(GET-CLASS (ICharacterHolder)), 
                         ICharacterHolder) .

Creating an object with constructor parameters

The factory supports the creation of instances using constructor parameters. Constructor parameters are passed as an object array to the CreateInstance method. The factory will pass holder types (e.g. Consultingwerk.CharacterHolder) using the contained value type. All other types of objects will be passed to the constructor of the implementing class.

In this example, 

DEFINE VARIABLE cArray   AS CHARACTER        NO-UNDO EXTENT 2.
ASSIGN cArray[1] = "for each eCustomer" 
       cArray[2] = "for each eSalesrep" .

oRequest         = CAST (oFactory:CreateInstance (GET-CLASS (IFetchDataRequest),
                                                  ArrayHelper:Array (NEW CharacterHolder ("eCustomer,eSalesRep"),
                                                                     NEW CharacterArrayHolder (cArray),
                                                                     NEW IntegerHolder (42))),
                        IFetchDataRequest) .

This will effectively be the same as

oRequest = NEW FetchDataRequest ("eCustomer,eSalesRep", cArray, 42).

Both methods will be invoking the following constructor:

CONSTRUCTOR PUBLIC FetchDataRequest (pcTables AS CHARACTER,
                                     pcQueries AS CHARACTER EXTENT,
                                     piNumRecords AS INTEGER):

Dependency on OpenEdge 11.6+

The ability to pass parameters to the implementing types constructor relies on ABL reflection, this feature is only available from OpenEdge 11.6 on. Prior to OpenEdge 11.6, an NotSupportedException will be thrown when passing an array of object parameters to the construtor.