Class Factories Overview (.NET and Delphi)

From RemObjects Wiki

Jump to:navigation, search

This is an Article about RemObjects SDK for .NET and Delphi
Feel free to add your notes to this topic below.



One of the goals of object-oriented design is to delegate responsibility among different objects. This kind of partitioning is good, since it encourages Encapsulation and Delegation.


Sometimes an application (or a framework) cannot anticipate the class of object that it must create. It will, anyways, receive some information that will allow it to understand how to act.


This is the situation a RemObjects server faces every time it receives a request from a client: the server needs to understand what object the request is directed to, it needs to create (or retrieve) an instance, and finally, at the end of the method call, it needs to decide what to do with the object instance.

The design we implemented to address these situations is a variation of the Factory Pattern.


Note: this article covers the implementation for both the Delphi and .NET editions of the RemObjects SDK. References that only apply to one edition are color coded appropriately.


Contents

Beyond database connection pooling

Most of the available Delphi frameworks are primarily data-centric. While this approach might be good for simple applications, it quickly falls short when you need to pool or provide thread-safe access to arbitrary objects.

The RemObjects SDK goes well beyond database connection pooling: it allows you to pool server object instances in a thread-safe manner. At the same time, you can choose to use a variety of other instantiation patterns for your objects.


How to control the instantiation model used

For Delphi, set the appropriate class-factory in the initialization section of your xxxx_Impl.pas units. This is an example taken from NewService_Impl.pas in the MegaDemo sample:

initialization
  TROClassFactory.Create('NewService', Create_NewService,
                         TNewService_Invoker);

TROClassFactory uses the "per-call" activation. This means that each time a request to the service "NewService" arrives, it creates a new instance, executes the request, and then destroys it. If you only wanted to create 4 instances of "NewService" and have those pooled between client requests, you would use this code:


initialization
  TROPooledClassFactory.Create('NewService', Create_NewService,
                               TNewService_Invoker, 4);


For .NET, the RemObjects SDK uses an attribute on the service implementation, e.g.

[RemObjects.SDK.Server.ClassFactories.StandardClassFactory()]
[RemObjects.SDK.Server.Service(Name="MegaDemoService",
                          InvokerClass=typeof(MegaDemoService_Invoker))]
public class MegaDemoService : RemObjects.SDK.Server.Service, IMegaDemoService {

Like the Delphi variant, the standard class factory create a new instance of the service per call, calls it, then releases it. To use pooling on .NET, you can change the attribute to use the PooledClassFactory attribute instead. For example, to create a pool of 4 instances:

[RemObjects.SDK.Server.ClassFactories.PooledClassFactory(4)]


Available class factories

The class factory code is located as follows:


The following instantiation models and class-factories are available in the SDK:

Model
Class Factory (Delphi)
Class Factory (.NET)
Per-call (default) TROClassFactory StandardClassFactory
Per-client TROPerClientClassFactory PerClientClassFactory
Singleton - note 1 TROSingletonClassFactory SingletonClassFactory
Synchronized Singleton TROSynchronizedSingletonClassFactory see note 2
Pooled TROPooledClassFactory PooledClassFactory


Notes:

  • 1) the Singleton model is not thread-safe like the other models. See its detail section below for more information.
  • 2) the Singleton model for .NET supports both synchronized and unsynchronized communications. For synchronized, you just need to set SingletonBehavior to Wait.



TROClassFactory / StandardClassFactory

This is the default class factory. Each time a request arrives, it creates a new instance of the server side object, executes the request and then frees it. Since each request acquires a separate instance of the object in its own thread, access to the members of the class is thread-safe. For instance, if you create a component inside the server object class, you can access it without having to worry about protecting it from other threads.

This instantiation model is the most scalable and is ideal in a web farm scenario.

For Delphi, its constructor is declared as follows:

constructor Create(const anInterfaceName: string;
                   aCreatorFunc: TRORemotableCreatorFunc;
                   anInvokerClass: TROInvokerClass);


For .NET, the attribute takes no parameters.


TROPerClientClassFactory / PerClientClassFactory

This class factory guarantees that a client will always access the same object in consecutive calls. The object will be instantiated the first time the client connects and it will remain alive until the object is not used for Timeout-seconds.

This instantiation model is less scalable and consumes more resources. We strongly discourage its use, but it can be useful when porting DataSnap applications.

For Delphi, you can also programmatically control the destruction of the objects by implementing the IRODestructorController interface and have the CanBeDestroyed function return TRUE. At the end of each call, the class factory will check for this value and act accordingly.

The TROPerClientClassFactory's constructor is declared as follows:

constructor Create(const anInterfaceName: string;
                   aCreatorFunc: TRORemotableCreatorFunc;
                   anInvokerClass: TROInvokerClass;
                   aTimeoutSeconds: cardinal);


For .NET, the PerClientClassFactory attribute takes no parameters, but has two fields:


TROSingletonClassFactory / SingletonClassFactory

This class factory creates only one instance of the object, and provides the requesting threads concurrent access to it. It is not thread-safe and access to internal members of the instance must be synchronized manually.

This instantiation model is very efficient but might be complex to handle. One instance may handle multiple simultaneous requests, so thread-safety will be a concern.

For Delphi, the TROSingletonClassFactory constructor is declared as follows:

constructor Create(const anInterfaceName: string;
                   aCreatorFunc: TRORemotableCreatorFunc;
                   anInvokerClass: TROInvokerClass);

For .NET, there are two overloaded constructors:

public SingletonClassFactoryAttribute()
public SingletonClassFactoryAttribute(SingletonBehavior aSingletonBehavior)
<source>

The {{bold|SingletonBehavior}} parameter can be set to Multiple, Wait or Fail. {{bold|Multiple}} (the default option) allows multiple clients to be running requests simultaneously on the same service instance. The {{bold|Wait}} option waits until the service instance is free to process the request, ensuring that there is only a single instance running at any one time. The {{bold|Fail}} option causes the call to fail when the service instance is already in use.



=={{class|TROSynchronizedSingletonClassFactory}}==

This class factory creates only one instance of the object, and provides the requesting threads serialized access to it. It is thread-safe and access to internal members of the instance is synchronized by the framework for you. 

This instantiation model is not very efficient but it can be useful when you need to serialize access to an external resource (e.g. a file or a host connection). 

The {{class|TROSynchronizedSingletonClassFactory}} constructor is declared as follows: 

<source lang="pas">
constructor Create(const anInterfaceName: string;
                   aCreatorFunc: TRORemotableCreatorFunc;
                   anInvokerClass: TROInvokerClass);


TROPooledClassFactory / PooledClassFactory

This class factory holds a predetermined number of object instances and it synchronizes access to the requesting threads.

For Delphi, the TROPooledClassFactory constructor is declared as follows:

constructor Create(const anInterfaceName: string;
                   aCreatorFunc: TRORemotableCreatorFunc;
                   anInvokerClass: TROInvokerClass;
                   aPoolSize: Integer;
                   aPoolBehavior:TROPoolBehavior=pbCreateAdditional;
                   aPreInitializePool:Boolean=false);


The pool can be fully populated when the factory is created by setting aPreInitializePool to TRUE.

When the requests reach the maximum number of items in the pool, the value of the PoolBehavior flag determines what happens: aPoolBehavior can be set to pbFail, pbWait, and pbCreateAdditional. Object instances are destroyed when the server is terminated.


For .NET, there are four overloaded constructors:

public PooledClassFactoryAttribute()
public PooledClassFactoryAttribute(int aPoolSize)
public PooledClassFactoryAttribute(int aPoolSize, PoolBehavior aPoolBehavior)
public PooledClassFactoryAttribute(int aPoolSize, PoolBehavior aPoolBehavior,
                                                      bool aPreInitializePool)


The PoolSize parameter defines the maximum size of the pool. The aPoolBehavior parameter can be set to:


The aPreIntializePool parameter will pre-allocate all aPoolSize service instances right at the start. By default, the pool size is 5, the behavior is Fail and PreInitializePool is false.


Examples

The RemObjects SDK is shipped with the following sample applications that demonstrate the use of class factories:


These examples can be found in the \RemObjects SDK for Delphi\Samples\ and the \RemObjects SDK for .NET\Samples\C#\ folders of your RemObjects SDK installation.


Ro-48.png

Product: RemObjects SDK
Available Editions: RemObjects SDK for .NET, Delphi, Xcode, Java and JavaScript

GlossaryArchitectureArticlesFeaturesLibrarySamples

Navigation
products
platforms
special
Toolbox