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-services4-components-node";
Not available
import (
    cbuild "github.com/pip-services4/pip-services4-go/pip-services4-components-go/build"
)
Not available
from pip_services4_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")
    }
}

Not available

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")
}
Not available
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"));
Not available
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"))
Not available
from pip_services4_components.refer import Descriptor

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();
Not available
component1.MyTask()
Not available
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 { Factory } from "pip-services4-components-node";
import { DefaultLogicFactory } from "pip-services4-logic-node";
Not available
import (
	cref "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
	clogic "github.com/pip-services4/pip-services4-go/pip-services4-logic-go/build"
)
Not available
from pip_services4_logic.lock import DefaultLockFactory
from pip_services4_components.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 DefaultLogicFactory();
Not available
logicFactory := clogic.NewDefaultLogicFactory()
Not available
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"));

Not available
lock, err := logicFactory.Create(cref.NewDescriptor("pip-services", "lock", "memory", "*", "1.0"))
Not available
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)

Not available
reflect.TypeOf(lock)
Not available
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-services4-components-node";
Not available
import (
    cbuild "github.com/pip-services4/pip-services4-go/pip-services4-components-go/build"
)
Not available
from pip_services4_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();
Not available
compositeFactory := cbuild.NewCompositeFactory()
Not available
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, Factory  } from "pip-services4-components-node";

let factory1 = new Factory()
factory1.registerAsType(new Descriptor("mygroup", "mycomponent1", "default", "*", "1.0"), MyComponent1)
compositeFactory.add(factory1)
Not available
factory1 := cbuild.NewFactory()
factory1.RegisterType(cref.NewDescriptor("mygroup", "mycomponent1", "default", "*", "1.0"), NewMyComponent1)
compositeFactory.Add(factory1)
Not available
from pip_services4_components.build import Factory
from pip_services4_components.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 { DefaultLogicFactory } from "pip-services4-logic-node";

compositeFactory.add(new DefaultLogicFactory());
Not available
import (
      cobserv "github.com/pip-services4/pip-services4-go/pip-services4-observability-go/build"
)

compositeFactory.Add(cobserv.NewDefaultObservabilityFactory())
Not available
from pip_services4_observability.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);
Not available
component1Locator := cref.NewDescriptor("*", "mycomponent1", "*", "*", "1.0")
component1, err := compositeFactory.Create(component1Locator)
Not available
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

Not available
loggerLocator := cref.NewDescriptor("*", "logger", "console", "*", "1.0")
result1, err := compositeFactory.Create(loggerLocator)
Not available
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.