Factories

How to create factories for components.

Key takeaways

IFactory Interface that defines methods used by component factories.
Factory Basic factory class that is used to create components via registered types and factory functions.
Default factories Factories that are built-into the Pip.Services toolkit, which can be used to create some of the most common component types.
CompositeFactory Component that is used to group multiple factories into a single component.

Introduction

This tutorial will teach you how to create factories for custom components, and how to use Pip.Service’s default factories to create instances of components already included in the toolkit. We will first examine the IFactory interface and its relations with the Factory and CompositeFactory components. Then, we will learn how to create a factory that helps build custom components and how to use the default factories to create components already included in the toolkit. Next, we will see how to create a composite factory that groups several factories together. Finally, we will review all the concepts learned.

IFactory

This interface declares methods that are required by factories to create components. The factories implementing this interface identify components by locators, which are usually defined by descriptors.

This interface is implemented by both the Factory and CompositeFactory classes. The diagram below shows their relations:

figure 1

Factory

This class represents a basic factory that creates components using registered types and factory functions.

To understand how this class works, we will use an example where we create a factory and then use it to create a custom component. The following sections will take us step-by-step through the construction of such a program.

Pre-requisites

In order to create a factory, we need to import the Factory class. This can be done with the following import statement:

import { Factory } from "pip-services3-components-nodex";
using PipServices3.Components.Build;
import (
	cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
)
import 'package:pip_services3_components/pip_services3_components.dart';
from pip_services3_components.build import Factory
Not available

Custom component

Next, we create a basic custom component. This component includes the myTask() method, which will be used later on in this tutorial. The code below shows what this class looks like:


export class MyComponent1 {
    private _status: string;
    /**
     * Creates a new instance of the component.
     */
    public constructor() {
        this._status = "Created";
        console.log("MyComponent1 has been created.");
    }

    public async myTask() {
        console.log("task executed")
    }
}

/// <summary>
/// Creating a process container
/// </summary>
class MyComponent1
{
    string _status;

    /// <summary>
    /// Creates a new instance of the component.
    /// </summary>
    public MyComponent1()
    {
        this._status = "Created";
        Console.WriteLine("MyComponent1 has been created.");
    }

    public void MyTask()
    {
        Console.WriteLine("task executed");
    }
}

import (
	"fmt"
)

type MyComponent1 struct {
	_status string
}

func NewMyComponent1() *MyComponent1 {
	c := &MyComponent1{}
	c._status = "Created"
	fmt.Println("MyComponent1 has been created.")
	return c
}

func MyTask() {
	fmt.Println("task executed")
}

class MyComponent1 {
  String _status;

  /// Creates a new instance of the component.
  MyComponent1() : _status = 'Created' {
    print('MyComponent1 has been created.');
  }

  void myTask() {
    print('task executed');
  }
}
class MyComponent1():
    
    def __init__(self):
        """
        Creates a new instance of the component.
        """
        self._status = "Created"
        print("MyComponent1 has been created.")
    
    def myTask(self):
        print("task executed")
Not available

Factory and component creation

Once we have our custom component defined, we create a factory for it by defining an instance of the Factory class. Next, we register our component within the factory instance and we use the factory’s create() method to create an instance of our custom component. The following code shows how to do this:

let factory = new Factory();
factory.registerAsType(new Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), MyComponent1);
let component1 = factory.create(new Descriptor("mygroup", "mycomponent1", "default", "name1", "1.0"));
var factory = new Factory();
factory.RegisterAsType(new Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), typeof(MyComponent1));
var component1 = factory.Create(new Descriptor("mygroup", "mycomponent1", "default", "name1", "1.0"));
factory := cbuild.NewFactory()
factory.RegisterType(cref.NewDescriptor("mygroup", "mycomponent1", "default", "*", "1.0"), NewMyComponent1)
component1, err = factory.Create(cref.NewDescriptor("mygroup", "mycomponent1", "default", "name1", "1.0"))
var factory = Factory();
factory.registerAsType(
    Descriptor('mygroup', 'mycomponent1', 'default', '*', '1.0'),
    MyComponent1);
var component1 = factory
    .create(Descriptor('mygroup', 'mycomponent1', 'default', 'name1', '1.0'));
factory = Factory()

factory.register_as_type(Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), MyComponent1)

component1 = factory.create(Descriptor("mygroup", "mycomponent1", "default", "name1", "1.0"))
Not available

Which, after running, produces the following outcome, confirming that an instance of MyComponent1 has been created.

figure 7

Once obtained, we can use this instance to, for example, call one of its methods:

component1.myTask();
component1.MyTask();
component1.MyTask()
component1.myTask();
component1.myTask()
Not available

Which, after running, produces the following result:

figure 3

Default factories

The Pip.Services toolkit contains several factories that can be used to create some common components. These factories are all subclasses of the Factory class. Among them are:

figure 4

Each of these factories has a set of default descriptors that can be employed to create components from the toolkit. The following table shows the main default factories, components they can create, and those components’ descriptors.

figure 5

Default factories and component creation

In order to learn how to use the toolkit’s default factories, we’ll explore the procedure for creating a memory lock using the default lock factory. This procedure applies to other default factories and their components as well.

For this, we need to first import the default lock factory. In addition, we need to import the Descriptor class, as we will use it to locate the memory lock. These actions can be done with the following import statements:

import { DefaultLockFactory } from "pip-services3-components-nodex";
import { Descriptor } from "pip-services3-commons-nodex";
using PipServices3.Components.Build;
using PipServices3.Commons.Refer;
import (
    cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	clock "github.com/pip-services3-gox/pip-services3-components-gox/lock"
)
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_components/pip_services3_components.dart';
from pip_services3_components.lock import DefaultLockFactory
from pip_services3_commons.refer import Descriptor
Not available

Once we have imported these two components, we can create an instance of the default lock factory.

let lockFactory = new DefaultLockFactory();
var lockFactory = new DefaultLockFactory();
lockFactory := clock.NewDefaultLockFactory()
var lockFactory = DefaultLockFactory();
lockFactory = DefaultLockFactory()
Not available

And then, we create an instance of MemoryLock by using the corresponding descriptor (see table above).

let lock = lockFactory.create(new Descriptor("pip-services", "lock", "memory", "*", "1.0"));
var lockk = lockFactory.Create(new Descriptor("pip-services", "lock", "memory", "*", "1.0"));
lock, err := lockFactory.Create(cref.NewDescriptor("pip-services", "lock", "memory", "*", "1.0"))
var lock = lockFactory
      .create(Descriptor('pip-services', 'lock', 'memory', '*', '1.0'));
lock = lockFactory.create(Descriptor("pip-services", "lock", "memory", "*", "1.0"))
Not available

We can verify that we have created a memory lock by checking the object’s type. The following command shows how to do this:

typeof(lock)
typeof(lock);
reflect.TypeOf(lock)
lock.runtimeType
type(lock)
Not available

Which, after running, returns the following output:

figure 6

CompositeFactory

This component allows us to group factories into one entity and then call the one(s) we need independently.

In this section’s example, we will build a component that contains two factories, namely, a factory for the custom component we created in the previous example and a factory for a logger.

Pre-requisites

In order to create a composite factory, we need to import the CompositeFactory class first. This can be done with the following import statement:

import { CompositeFactory } from "pip-services3-components-nodex";
using PipServices3.Components.Build;
import cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
import 'package:pip_services3_components/pip_services3_components.dart';
from pip_services3_components.build import CompositeFactory
Not available

Composite factory

Once we have imported the class, we can create an instance of it.

let compositeFactory = new CompositeFactory();
var compositeFactory = new CompositeFactory();
compositeFactory := cbuild.NewCompositeFactory()
var compositeFactory = CompositeFactory([]);
compositeFactory = CompositeFactory()
Not available

Now, we create a factory for the custom component defined in the previous example. Then, we register the component and add its factory to the composite factory. The code below shows how to do this:

import { Descriptor } from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";

let factory1 = new Factory()
factory1.registerAsType(new Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), MyComponent1)
compositeFactory.add(factory1)
using PipServices3.Commons.Refer;
using PipServices3.Components.Build;

var factory1 = new Factory();
factory1.RegisterAsType(new Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), typeof(MyComponent1));
compositeFactory.Add(factory1);

import (
    cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
)

factory1 := cbuild.NewFactory()
factory1.RegisterType(cref.NewDescriptor("mygroup", "mycomponent1", "default", "*", "1.0"), NewMyComponent1)
compositeFactory.Add(factory1)
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_components/pip_services3_components.dart';

var factory1 = Factory();
factory1.registerAsType(Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), MyComponent1);
compositeFactory.add(factory1);
from pip_services3_components.build import Factory
from pip_services3_commons.refer import Descriptor

factory1 = Factory()

factory1.register_as_type(Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), MyComponent1)

compositeFactory.add(factory1)
Not available

Next, we include a factory for a logger by adding the default logger factory.

import { DefaultLoggerFactory } from "pip-services3-components-nodex";

compositeFactory.add(new DefaultLoggerFactory());
using PipServices3.Components.Log;

compositeFactory.Add(new DefaultLoggerFactory());
import (
    clog "github.com/pip-services3-gox/pip-services3-components-gox/log"
)

compositeFactory.Add(clog.NewDefaultLoggerFactory())
import 'package:pip_services3_components/pip_services3_components.dart';

compositeFactory.add(DefaultLoggerFactory());
from pip_services3_components.log import DefaultLoggerFactory

compositeFactory.add(DefaultLoggerFactory())
Not available

Once we’ve added all our factories to the composite factory, we can call the latter to create an instance of our custom component via its descriptor. The following code shows how this can be done:

let component1Locator = new Descriptor("*", "mycomponent1", "*", "*", "1.0");
component1 = compositeFactory.create(component1Locator);
var component1Locator = new Descriptor("*", "mycomponent1", "*", "*", "1.0");
component1 = compositeFactory.Create(component1Locator);
component1Locator := cref.NewDescriptor("*", "mycomponent1", "*", "*", "1.0")
component1, err := compositeFactory.Create(component1Locator)
var component1Locator = Descriptor('*', 'mycomponent1', '*', '*', '1.0');
var component1 = compositeFactory.create(component1Locator);
component1Locator = Descriptor("*", "mycomponent1", "*", "*", "1.0")
component1 = compositeFactory.create(component1Locator)
Not available

Which, after running, produces the following result:

figure 7

Similarly, we can call the same composite factory to create an instance of a logger. The following code shows this step:

let loggerLocator = new Descriptor("*", "logger", "console", "*", "1.0");

let result1 = compositeFactory.create(loggerLocator);
result1
var loggerLocator = new Descriptor("*", "logger", "console", "*", "1.0");

var result1 = compositeFactory.Create(loggerLocator);
result1
loggerLocator := cref.NewDescriptor("*", "logger", "console", "*", "1.0")
result1, err := compositeFactory.Create(loggerLocator)
result1
var loggerLocator = Descriptor('*', 'logger', 'console', '*', '1.0');
var result1 = compositeFactory.create(loggerLocator);

result1
loggerLocator = Descriptor("*", "logger", "console", "*", "1.0")

result1 = compositeFactory.create(loggerLocator)
result1
Not available

Which, after running, provides the following result that shows that an instance of a logger was created:

figure 8

Wrapping up

In this tutorial, we have learned how to create a factory that can be used to build a custom component, and how to use Pip.Services’ default factories. We have examined the composite factory, which can be made to include one or more factories and can be called to create instances of components that the included factories are capable of creating.