Creatio development guide
PDF
This documentation is valid for Creatio version 7.16.0. We recommend using the newest version of Creatio documentation.

Replacement class object factory

Glossary Item Box

Introduction

Creatio development is based on the main practices of object-oriented programming, including the open-closed principle. According to this approach, all entities (classes, modules, functions, etc.) should be open for extension but closed for modification. This means that new logic and features must be implemented by introducing new entities rather than modifying existing ones. The open-closed principle is the basis of the Creatio package mechanism.

Configuration elements in the pre-installed packages cannot be modified. Developing additional functionality and modifying the existing functionality is done solely via custom packages. Learn more about working with packages in the “Development tools. Packages” block of articles.

Customizing the configuration may require changing or extending the base functionality. Retrofitting the logic of preinstalled objects is restricted system-wide. The user should use the replacement mechanism to solve such problems.

To change the behavior of a preinstalled object, create a new custom package object that extends the preinstalled object. For the new object, set a property implying that the object will replace the parent object in the object hierarchy. All modifications that have to be applied to the preinstalled object are to be implemented in the replacement object. When calling the preinstalled object, the system will then run the logic of the corresponding replacement object.

The same base object can be replaced in multiple custom packages. The resulting replacing object implementation in the compiled configuration is based on the hierarchy of packages that contain the replacing objects.

Replacement mechanism description

Creating a replacing configuration element

To create a replacing configuration element:

  1. In a custom package that will contain the replacing entity, set a dependency on the package that contains the replaceable entity.
  2. Create a replacing object or replacing page. To create a replacing object, run [Add] –> [Replacing Object] in the [Configuration] section. To create a replacing page, run [Add] –> [Replacing Page] in the [Configuration] section.

Creating a replacement class

Specific configuration tasks may require modification of the program code of such schemas as [Source Code]. In that case, use classes implemented in the schemas of custom packages as replacement objects.

Replacement classes follow roughly the same procedure as replacement configuration elements, however, creating and using replacement class instances has certain specifics.

To create a replacement class:

  1. In a custom package that will contain the replacement class, set a dependency on the package that contains the class to replace.
  2. In a custom package, create a replacement class that extends the replaceable class.
  3. Mark the replacement class with an [Override] attribute.
  4. Implement the logic of the replacement class (for example, add new properties and methods that extend the functionality of the replaceable class, overload a method of the replaceable class to change its behavior, etc).

A replacement class is instantiated by the replacement class object factory. The factory is queried for an instance of the replaceable class, and then returns an instance of a corresponding replacement class, which is computed using a dependency tree in the source code schemas.

The specifics of the implementation and use of the factory are described below, as well as a use case for creating and using replacement class instances.

Replacement class object factory

Override attribute

The [Override] attribute type is described in the Terrasoft.Core.Factories namespace. The attribute extends the System.Attribute base type and will only apply to classes. Classes marked with this attribute are used by the factory to build a dependency tree of replacable and replacement classes.

A use case of applying the [Override] attribute to the MySubstituteClass, which replaces SubstitutableClass, is available below.

[Override]
public class MySubstituteClass : SubstitutableClass
{
    // Class implementation.
}

Replacement factory details. The ClassFactory class

The ClassFactory static class is an implementation of a software factory for Creatio replacement objects. The software factory relies on the Ninject open-source framework for dependency injection. Essentially, the factory collects data about all replaceable configuration types during the initialization phase and configures the framework core accordingly. The framework then returns instances of the required types based on the configured dependencies.

The factory is initialized at the moment of the first call, i.e. when attempting to retrieve an instance of a replacement. This is what happens during the call:

  1. The replacement factory checks if replacement types are found among the types of the configuration build. The factory interprets a descendant class marked with the [Override] attribute as a replacement class and the parent as a replaceable class. The factory builds a dependency tree as a list of [Replaced type] –> [Replacing type] pairs. Transitional types are not accounted for in the replacement tree. As a result, the origin class is replaced with the last descendant in the replacement hierarchy.

    You can find a class hierarchy below.

    // Source class.
    public class ClassA { }
     
    // Class that replaces ClassА.
    [Override]
    public class ClassB : ClassA { }
     
    // Class that replaces ClassВ.
    [Override]
    public class ClassC : ClassB { }
    

    Using this hierarchy, the replacement factory will build the following dependency tree:
    ClassA —> ClassC
    ClassB —> ClassC
    Then, the type replacement hierarchy will look like this: ClassA —> ClassB —> ClassC. That means, ClassA will be replaced with ClassC, which is the last descendant in the replacement hierarchy, not with the transitional ClassB type. Consequently, when queried for a ClassA or ClassB instance, the replacement factory will return a ClassC instance.
  2. Using Ninject, the factory binds replacement types based on the constructed type dependency tree.

Creating a replaceable type instance

ClassFactory provides the Get<T> parametrized public static method to get an instance of a replacement type. The replaceable type serves as a generic method parameter.

Getting an instance of a class that replaces the SubstitutableClass type is covered below. Explicit typing of a new class instance is unnecessary. Due to the preliminary initialization, the factory will determine what type will substitute the replaceable type, and then create an instance of the replacement type.

var substituteObject = ClassFactory.Get<SubstitutableClass>();

The Get<T> method can accept an array of ConstructorArgument objects as an input parameter. Each of the objects is an argument for the constructor of the class created by the factory. This way, the factory enables instantiation of replacement objects with parameterized constructors. The factory core independently resolves all of the dependencies required for the creation or operation of the object.

In most situations, we recommend that the constructors of the replacement classes have the exactly same signature as the parent class. If the implementation logic of a replacement class requires declaring a constructor with a custom signature, make sure to comply with the following rules for creating and calling the constructors of the replaceable and replacement classes:

  1. If the replaceable class does not have an explicitly parameterized constructor (the class only has a default constructor), then the replacement class may have an explicitly parameterized constructor without any restrictions. The standard order of calls to the constructors of the parent and child classes must be respected. When instantiating the original (replaceable) class via the factory, make sure to pass the correct parameters to the factory to initialize the properties of the class that will eventually replace the original one. Failure to comply with this rule will result in a runtime error.
  2. If the replaceable class has a parameterized constructor, then the replacement class must implement the constructor of the parent class. The constructor of the replacement class must explicitly call the parameterized constructor of the parent (replaceable) class and pass parameters for the correct initialization of the parent properties. Other than that, the constructor of the replacement class may initialize own properties or remain empty. Failure to comply with this rule will result in a runtime error. The responsibility for the correct initialization of the properties of the replaceable and replacement objects ultimately lies with the developer. Below is a series of sample cases that illustrate instantiation cases of replaceable types with parameterized constructors.

Cases

Case example 1

The SubstitutableClass replaceable class has one default constructor. Two constructors are defined in the SubstituteClass replacement class: a default constructor and a parameterized constructor. The replacement class overloads the GetMultipliedValue() parent method. Options for instantiating and invoking the GetMultipliedValue() method with the default and parameterized constructors are listed below.

// Declaring a replaceable class.
public class SubstitutableClass
{
    // Class property to initialize in the constructor.
    public int OriginalValue { get; private set; }
 
    // Default constructor, which initializes the OriginalValue property with 10.
    public SubstitutableClass()
    {
        OriginalValue = 10;
    }
 
    // The method returns OriginalValue multiplied by 2.
    // This method can be reloaded
    // reloaded in replacement classes.
    public virtual int GetMultipliedValue()
    {
        return OriginalValue * 2;
    }
}
 
// Declaring a class to replace SubstitutableClass.
[Terrasoft.Core.Factories.Override]
public class SubstituteClass : SubstitutableClass
{
    // SubstituteClass property.
    public int AdditionalValue { get; private set; }
 
    // Default constructor, which initializes the AdditionalValue property with 15. Calling 
    // the constructor of the SubstitutableClass parent class is not required, since the parent class only provides
    // the default constructor (called implicitly when instantiating SubstituteClass).
    public SubstituteClass()
    {
        AdditionalValue = 15;
    }
 
    // Parameterized constructor, which initializes the AdditionalValue property with a value 
    // passed as the parameter. Likewise, the parent constructor is not called explicitly.
    public SubstituteClass(int paramValue)
    {
        AdditionalValue = paramValue;
    }
 
    // Replacing a parent method. // The method returns AdditionalValue multiplied by 3.
    public override int GetMultipliedValue()
    {
        return AdditionalValue * 3;
    }
}

Options for getting an instance of the replacement class using the factory are listed below.

// Getting an instance of the class that replaces SubstitutableClass.
// The factory will return an instance of SubstituteClass initialized by the constructor without parameters.
var substituteObject = ClassFactory.Get<SubstitutableClass>();
 
// The variable value will equal 10. The OriginalValue property is initialized by the default
// parent constructor, which is implicitly called during the instantiation of the replacement class.
var originalValue = substituteObject.OriginalValue;
 
// A call to the replacement class method that will return the value of AdditionalValue
//Multiplied by 3. The variable value will equal 45 since AdditionalValue is 
// initialized with 15.
var additionalValue = substituteObject.GetMultipliedValue();
 
// Getting an instance of the replacement class initialized with a parameterized 
// constructor. Note that the name of the ConstructorArgument parameter must be the same as the name of 
// the parameter in the class constructor.
var substituteObjectWithParameters = ClassFactory.Get<SubstitutableClass>(
            new ConstructorArgument("paramValue", 20));
 
// The variable value will equal 10.
var originalValueParametrized = substituteObjectWithParameters.OriginalValue;
 
// The variable value will equal 60 since AdditionalValue is 
// initialized 20.
var additionalValueParametrized = substituteObjectWithParameters.GetMultipliedValue();

Case example 2

The SubstitutableClass replaceable class has one parameterized constructor. The SubstituteClass replacement class has one parameterized constructor as well.

// Declaring a replaceable class
public class SubstitutableClass
{
    // Class property to initialize in the constructor
    public int OriginalValue { get; private set; }
 
    // Parameterized constructor, which initializes the OriginalValue property with a value
    // passed as the originalParamValue parameter
    public SubstitutableClass(int originalParamValue)
    {
        OriginalValue = originalParamValue;
    }
 
    // The method returns OriginalValue multiplied by 2. // This method can be reloaded
    // reloaded in replacement classes
    public virtual int GetMultipliedValue()
    {
        return OriginalValue * 2;
    }
}
 
// Declaring a class to replace SubstitutableClass.
[Terrasoft.Core.Factories.Override]
public class SubstituteClass : SubstitutableClass
{
    // SubstituteClass property.
    public int AdditionalValue { get; private set; }
 
    // Parameterized constructor, which initializes the AdditionalValue property with a value 
    // passed as the parameter. You must call the parent constructor explicitly to initialize the 
    // the parent property. If you do not do that, the compilation will end with an error.
    public SubstituteClass(int paramValue) : base(paramValue + 8)
    {
        AdditionalValue = paramValue;
    }
 
    // Replacing a parent method. // The method returns AdditionalValue multiplied by 3.
    public override int GetMultipliedValue()
    {
        return AdditionalValue * 3;
    }
}

Below, you can find an example of getting and using an instance of the replacement class using the factory.

// Getting an instance of the replacement class initialized with a parameterized
// constructor. Note that the name of the ConstructorArgument parameter must be the same as the name of
// the parameter in the class constructor.
var substituteObjectWithParameters = ClassFactory.Get<SubstitutableClass>(
            new ConstructorArgument("paramValue", 10));
 
// The variable value will equal 18.
var originalValueParametrized = substituteObjectWithParameters.OriginalValue;
 
// The variable value will equal 30.
var additionalValueParametrized = substituteObjectWithParameters.GetMultipliedValue();

Case example 3

The SubstitutableClass replaceable class has one parameterized constructor as well. The replacement SubstituteClass class does not introduce any new properties but reloads the GetMultipliedValue() method, which will now return a fixed value. The SubstituteClass class does not require the initialization of its properties. It must explicitly declare a constructor that calls the parent constructor with the parameters to initialize the parent properties correctly.

// Declaring a replaceable class
public class SubstitutableClass
{
    // Class property to initialize in the constructor
    public int OriginalValue { get; private set; }
 
    // Parameterized constructor, which initializes the OriginalValue property with a value
    // passed as the originalParamValue parameter
    public SubstitutableClass(int originalParamValue)
    {
        OriginalValue = originalParamValue;
    }
 
    // The method returns OriginalValue multiplied by 2. // This method can be reloaded
    // reloaded in replacement classes
    public virtual int GetMultipliedValue()
    {
        return OriginalValue * 2;
    }
}
 
// Declaring a class to replace SubstitutableClass.
[Terrasoft.Core.Factories.Override]
public class SubstituteClass : SubstitutableClass
{
    // Empty default constructor that calls the parent class constructor explicitly
    // to initialize the parent properties correctly.
    public SubstituteClass() : base(0)
    {
    }
 
    // You can also use an empty parameterized constructor to pass the parameters
    // to the parent class constructor.
    public SubstituteClass(int someValue) : base(someValue)
    {
    }
 
    // Replacing a parent method. The method will return the hardcoded value.
    public override int GetMultipliedValue()
    {
        return 111;
    }
}

You can find an example of creating a replacement class in the "Creating replacement classes in packages" article.

© Creatio 2002-2020.

Did you find this information useful?

How can we improve it?