Factory

Description

Factories implement types. They are used to create objects.

Each factory is identified by a unique factory identifier. A factory identifier is a prefixed identifier starting with fa_ as prefix. For more information about prefixed identifiers see the section called “Prefixed identifier”.

One factory can only create objects of one type, namely the type which is implemented by the factory.

In most cases there exists only one factory for a given type, and all objects of that type are created with that factory. However, there can be an unlimited number of factories implementing the same type in different ways. For example, one factory could be optimized for low memory usage, while another one could be optimized for best performance. Different factories implementing the same type must have different identifiers.

An object is created through a creator defined in the factory. A creator is similar to a command. It can have 0 or more input arguments, but it must have at least one output argument that represents the object to be created. Each factory has at least one creator. But it can have additional alternative creators, so that objects can be created in different ways. Different creators must have different identifiers.

The relations between types, factories and creators is shown in the following figure:

Figure 17.1. Types, factories and creators

Types, factories and creators

Each attribute and each command defined in a type must be implemented in the factory. However, if an attribute's implementation is not explicitly defined in the factory, then the compiler provides a default implementation, which consists of storing the attribute's value as an object in memory. As this is the most frequent way to implement attributes, the programmer is often released from writing code to implement attributes in factories.

Sometimes a factory needs to manage additional attributes which are not defined in the type implemented by the factory, but which are needed by the factory. Such attributes are called private attributes, and a factory can contain any number of private attributes. Private attributes cannot be accessed from outside the factory.

The same is true for commands. A factory can contain any number of private commands which cannot be accessed from outside, but which are needed for implementation.

Syntax

Table 17.2. Factory

ProductionSyntaxLinks
factory

"factory" ( "id" ":" ) ? factory_id "type" ":" type_selector
   RSE_java_code ?
   attribute_list ?
   command_list ?
   creator_list
   RSE_test_script ?
"end" "factory" ?

the section called “Factory”

Examples

Example 17.4. Factory supplier

Suppose we want to implement the following simple type supplier with two attributes and a command that returns a XML representation of the supplier:

type supplier

   attribute identifier type:positive32 end
   attribute name type:string end
   
   command convert_to_XML
      out result type:string end
   end command

end type

Let's look at a first approach for a factory implementing the above type.

As the Obix compiler provides a default implementation for attributes, we don't need to explicitly define their implementation.

Each factory must provide at least one creator. A creator with an input argument for each attribute defined in the type could be defined as:

   creator create
      in identifier type:positive32 end
      in name type:string end

      out result type:supplier end
      
      script
         o_result.a_identifier = i_identifier
         o_result.a_name = i_name
      end script
   end creator
   

As we can see, the creator's script simply assigns the input arguments to the attributes of output argument o_result.

Many factories contain a creator like the above one, namely a creator with one input argument for each attribute defined in the type, and one output argument which is the object to be created by the factory. Therefore, the compiler can be told to automatically build such a creator with the kind:in_all clause. Hence, our above creator can simply be rewritten as:

   creator create kind:in_all end

For more information about creators see the section called “Creator”

A factory must implement each command defined in the type. Command convert_to_XML can be written as:

   command convert_to_XML
      script
         o_result = """
             <supplier>
               <id>{{a_identifier.co_to_string}}</id>
               <name>{{a_name}}</name>
             </supplier>"""
      end script
   end command

For explanations about the above code please refer to Chapter 3, Simple example

The complete code for the factory looks as follows:

factory supplier_1 type:supplier

   command convert_to_XML
      script
         o_result = """
             <supplier>
               <id>{{a_identifier.co_to_string}}</id>
               <name>{{a_name}}</name>
             </supplier>"""
      end script
   end command

   creator create kind:in_all end

end factory

A supplier can now be created with:

var ty_supplier v_supplier = fa_supplier_1.co_create ( &
   i_identifier = 123 &
   i_name = "IT FOR EVERYONE" )

And the following instruction:

se_system.console.write_line ( v_supplier.co_convert_to_XML )

will display the following on the system console:

             <supplier>
                <id>123</id>
                <name>IT FOR EVERYONE</name>
             <supplier>

Suppose now we want to be able to define if the XML code produced by command convert_to_XML uses XML tags, as in the above example, or XML attributes, so that the output would be:

<supplier id="123" name="IT FOR EVERYONE" />

There are several solutions to this requirement.

The most obvious one would be to add an input argument to command convert_to_XML defined in the type:

type supplier_2

   attribute identifier type:positive32 end
   attribute name type:string end
   
   command convert_to_XML
      in use_XML_attributes type:yes_no end
      out result type:string end
   end command

end type

The factory now has to consider the value of input argument use_XML_attributes and could be written as:

factory supplier_2 type:supplier_2

   command convert_to_XML
      script
         if i_use_XML_attributes then
            o_result = """<supplier id="{{a_identifier.co_to_string}}" name="{{a_name}}" />"""
         else
            o_result = """
              <supplier>
                <id>{{a_identifier.co_to_string}}</id>
                <name>{{a_name}}</name>
              </supplier>"""
         end if
      end script
   end command
   
   creator create kind:in_all end

end factory

Although the above solution would be the one to choose in many cases, we have to consider that modifying a type is not always possible or desirable. For example we might not have access to the type's source code, because it is defined in a public third-party library. Another problem could be that all other factories implementing the type (or child types) would have to be adapted to take care of the new input argument.

Therefore, let's have a look at different solutions, so that we can explore several ways for defining factories.

In case type supplier cannot be modified, one solution would be to simply define a second factory implementing the same type, but in a different way (i.e. using XML attributes instead of XML tags):

Example 17.5. Another factory implementing the same type

factory supplier_factory_using_XML_attributes type:supplier

   command convert_to_XML
      script
         o_result = """<supplier id="{{a_identifier.co_to_string}}" name="{{a_name}}" />"""
      end script
   end command
   
   creator create kind:in_all end

end factory

Now the programmer can write

var ty_supplier v_supplier = fa_supplier_1.co_create ( &
   i_identifier = 123 &
   i_name = "IT FOR EVERYONE" )

if tags should be used for XML

or he/she can write

var ty_supplier v_supplier = fa_supplier_factory_using_XML_attributes.co_create ( &
   i_identifier = 123 &
   i_name = "IT FOR EVERYONE" )

to use XML attributes instead of XML tags


Another solution would be to stay with one factory but to add a second creator that is used whenever we want to use XML attributes. In that case we also need a private attribute that remembers how to create the XML.

Example 17.6. Two creators and a private attribute in a factory

factory supplier_3 type:supplier

   // private attribute that remembers how to create XML
   // default value is no, which means: use XML tags
   attribute use_XML_attributes type:yes_no default: no private:yes end

   command convert_to_XML
      script
         // use XML tags or XML attributes, depending on the value stored in private attribute use_XML_attributes
         if a_use_XML_attributes then
            o_result = """<supplier id="{{a_identifier.co_to_string}}" name="{{a_name}}" />"""
         else
            result = """
              <supplier>
                <id>{{a_identifier.co_to_string}}</id>
                <name>{{a_name}}</name>
              </supplier>"""
         end if
      end script
   end command
   
   // standard creator to use XML tags
   creator create kind:in_all end

   // alternative creator to use XML attributes
   creator create_using_XML_attributes kind:in_all
      script // this time we have to explicitly define the script, because the default implementation isn't adequate
         o_result.a_identifier = i_identifier
         o_result.a_name = i_name
         o_result.a_use_XML_attributes = yes // overwrite default value of attribute use_XML_attributes
      end
   end

end factory

This time we write

var supplier supplier = supplier_3.create ( &
   identifier = 123 &
   name = "IT FOR EVERYONE" )

to use XML tags, or

var supplier supplier = supplier_3.create_using_XML_attributes ( &
   identifier = 123 &
   name = "IT FOR EVERYONE" )

to use XML attributes.


Another alternative would be to use only one creator, but to add an input argument that enables us to specify how the XML should be created.

Example 17.7. Creator with additional input argument

factory supplier_4 type:supplier

   // private attribute that remembers how to create XML
   attribute use_XML_attributes type:yes_no private:yes end

   command convert_to_XML
      script
         // use XML tags or XML attributes, depending on the value stored in private attribute use_XML_attributes
         if a_use_XML_attributes then
            o_result = """<supplier id="{{a_identifier.co_to_string}}" name="{{a_name}}" />"""
         else
            o_result = """
              <supplier>
                <id>{{a_identifier.co_to_string}}</id>
                <name>{{a_name}}</name>
              </supplier>"""
         end if
      end script
   end command
   
   creator create
      in identifier type:positive32 end
      in name type:string end
      in use_XML_attributes type:yes_no default:no end

      out result type:supplier end
      
      script
         o_result.a_identifier = i_identifier
         o_result.a_name = i_name
         o_result.a_use_XML_attributes = i_use_XML_attributes
      end
   end

end factory

To use XML tags, we now write:

var supplier supplier = supplier_4.create ( &
   identifier = 123 &
   name = "IT FOR EVERYONE" &
   use_XML_attributes = no )

or, more simply (because the default value for i_use_XML_attributes is no):

var supplier supplier = supplier_4.create ( &
   identifier = 123 &
   name = "IT FOR EVERYONE" )

And to use XML attributes we code:

var supplier supplier = supplier_4.create ( &
   identifier = 123 &
   name = "IT FOR EVERYONE" &
   use_XML_attributes = yes )

As we can see, there are several ways for factories to create objects. It is up to the programmer to decide which is the best solution for a given type.

But let us come back to our initial factory that uses XML tags:

factory supplier_1 type:supplier

   command convert_to_XML
      script
         o_result = """
             <supplier>
               <id>{{a_identifier.co_to_string}}</id>
               <name>{{a_name}}</name>
             </supplier>"""
      end script
   end command

   creator create kind:in_all end

end factory

Looking at the above script (where the XML tags are created), we can easily see code duplication, one of the biggest enemies in software development. To avoid this, we can add a private command as follows:

Example 17.8. Factory with private command

factory supplier_5 type:supplier

   // a private command used to create XML tags
   // this command can only be used within this factory
   command create_XML_tag private:yes
      in tag type:string end
      in data type:string end

      out result type:string end

      script
         o_result = """<{{tag}}>{{data}}</{{tag}}>"""
      end
   end

   command convert_to_XML
      script
         // use private command create_XML_tag in order to avoid code duplication
         o_result = """
            <supplier>
              {{ create_XML_tag ( tag = "id"; data = a_identifier.co_to_string ) }}
              {{ create_XML_tag ( tag = "name"; data = a_name ) }}
            </supplier>"""
      end script
   end command
   
   creator create kind:in_all end

end factory

Creating XML tags is certainly something we will use in other factories too. Therefore, instead of defining a private command in the above factory, it would be much better to transfer that utility to a service, so that the functionality can easily be used by other software elements. Please refer to Example 17.10, “XML utilities service” to see how such a service could be defined.

Using that service, our factory now finally becomes:

Example 17.9. Factory supplier using a service

factory supplier type:supplier

   command convert_to_XML
      script
         var string id_tag = se_XML_utility.create_XML_tag ( &
            tag = "id" &
            data = a_identifier.co_to_string )
         var string name_tag = se_XML_utility.create_XML_tag ( &
            tag = "name" &
            data = a_name )
         o_result = se_XML_utility.create_XML_tag ( &
            tag = "supplier" &
            data = v_id_tag & v_name_tag )
      end script
   end command
   
   creator create kind:in_all end

end factory

See also