Design patterns - vilinski/nemerle GitHub Wiki

Table of Contents

Intro

Design patterns are meant to ease a programmers work by distinguishing the various common schemas of creating software and solving the most frequently occuring problems. Most of them involve some special interaction between objects in a program, which can be easily identified and named.

The downside of many design patterns is that they often require implementing massive amounts of code, which tends to be always the same or similar. Some patterns only point out how particular objects could communicate, or what the class inheritance hierarchy representing the given model should be. These patterns are only hints for programmers, as to what the preferred ways of structuring their programs in specific situations are. Unfortunately, others may demand large amounts of mindless work for programmers trying to follow them. They need to override the n-th method from the k-th class by adding the same code as in all 0..n-1 previous methods. This kind of task is not only boring, but also a waste of a developer's time, and can be a nightmare to maintain.

Metaprogramming provides a solution. We can simply write a generator for all those methods, wrappers, auxiliary instances, etc. In this document we present examples of choosen design patterns, where often repeated code is generated instead of hand-written.

Abstract factory pattern

(provided in standard macros for Nemerle 0.9.4)

Abstract factory pattern is a way to make construction of objects customizable. It is based on using virtual methods to create instances of some types instead of directly calling constructors.

The factory pattern macro is used to generate those factory methods as simple wrappers of given type's constructors. It allows to change code like:

class Factory 
{
  public virtual CreateWidget (name : string) : Widget {
    Widget (x)
  }
  public virtual CreateWorker (name : string, tasks : int) : Worker {
    Worker (name, tasks)
  }
}

class MyFactory : Factory 
{
  public override CreateWidget (name : string) : Widget {
    MyWidget (x)
  }
  public override CreateWorker (name : string, tasks : int) : Worker {
    MyWorker (name, tasks)
  }
}

into

[Nemerle.DesignPatterns.AbstractFactory (Widget, Worker)]
class Factory {}

[Nemerle.DesignPatterns.AbstractFactory (Override (MyWidget, Widget), Override (MyWorker, Worker))]
class MyFactory : Factory {}

Features

  • considers all public constructors in given classes
  • supports generic classes, which can be additionally constrained with concrete arguments, like
[AbstractFactory (IntsDictionary [int, _])]
``` 

creates method

 ```nemerle
public virtual CreateIntsDictionary [T] () : IntsDictionary [int, T] { ... }
  • you can add any methods / implementation of your own to the factory classes

Limitations

  • constructors in overriding class must match exactly the virtual methods in base factory class, otherwise you will get error that method tries to override non-existing base method
  • only constructors explicitly provided (or the default empty ctor) are considered (so you cannot use it with types decorated with Record macro) - though it works fine with externally referenced types (from other assemblies)

Aggregation (Composite) pattern

The aggregation pattern (or maybe more closely composite pattern) allows mixing together different components into single one, which is the sum of them (by mean of provided functionality). You can also view it as a cleaner and more reliable implementation of multiple inheritance.

The idea is to create a container object, which contains references to various sub-modules in its instance fields. Then it also defines methods, which are actually the simple proxies to those sub-modules. In effect, the container object provides the same public members as aggregated objects, which can be used to implement the same interfaces as sub-modules.

A sample code using our Aggregate macro is as follows:

class ReadUtil {
    ...
    public ReadFile () : string { ... }
}
class WriteUtil {
    ...
    public WriteFile (contents : string) : void { ... }
}

[Nemerle.DesignPatterns.Aggregate (ReadUtil, WriteUtil)]
class ReadWriteUtil { 
  public this (inp : string, outp : string) {
    _initReadUtil (inp);   
    _initWriteUtil (outp);   
  }

  Run () : void {
    this.WriteFile (this.ReadFile ());
  }
}

(see the full example)

and is transformed to

class ReadWriteUtil {
  _ReadUtil : ReadUtil;
  _WriteUtil : WriteUtil;

  public ReadFile () : string { _ReadUtil.ReadFile () }
  public WriteFile (x : string) : void { _WriteUtil.WriteFile (x) }
  _initReadUtil (name : string) : void { _ReadUtil = ReadUtil (x); }
  ...
}

Features

  • defines initialization methods for constructing aggregated objects (and assigns them to private container fields) - those methods are generated as delegates to constructors (similar to abstract factory pattern)
  • creates proxy methods and properties (internally it just uses the ProxyPublicMembers macro)

Limitations

  • does not support resolving conflicts between proxy methods/properties, they must all sum into valid overloaded methods list
  • just like in abstract factory pattern, only explicit constructors are considered (or default ctor if none are defined)

Proxy design pattern

Proxy pattern is based on forwarding calls to some object A into calls to another object B. Usually the object B is contained in an instance field of A. The point of this is to imitate behavior of B in a new class. We can even implement Bs interface in A by simply passing all calls to B. We can then override some of these methods with new behaviour.

The solution presented is also very similar to implicit interface implementation through aggregation, one of many niched suggestions about C# language.

What we want to achieve

Suppose we have an interface IMath and an object Math implementing this interface. Math will be the object stored on server and we will create a representative object that controls access to it in a different AppDomain.

using System;
using System.Runtime.Remoting;

// "Subject"

public interface IMath
{
  // Methods
   Add( x :  double, y : double ) : double;
   Sub( x :  double, y : double ) : double;
   Mul( x :  double, y : double ) : double;
   Div( x :  double, y : double ) : double;
}

// "RealSubject"

class Math : MarshalByRefObject, IMath
{
  // Methods
  public Add(x : double, y : double ) : double { x + y; }
  public Sub(x : double, y : double ) : double { x - y; }
  public Mul(x : double, y : double ) : double { x * y; }
  public Div(x : double, y : double ) : double { x / y; }
}

Now for accessing Math in different AppDomain we create a MathProxy, which will provide full functionality of IMath. Internally it will use Math instance to implement all that functionality.

The point is that forwarding every call in IMath requires a considerable amount of code to be written. We will use a macro, which generates needed methods automatically.

// Remote "Proxy Object"
class MathProxy : IMath
{
  // the stubs implementing IMath by calling math.* are automatically generated
  // (that macro is not included in the standard library;
  // there is another proxy macro down the page)
  [DesignPatterns.Proxy (IMath)]
  math : Math;
  
  // Constructors
  public this()
  {
    // Create Math instance in a different AppDomain
    def ad = System.AppDomain.CreateDomain( "MathDomain",null, null );
    def o =
      ad.CreateInstance("Proxy_RealWorld", "Math", false,
                        System.Reflection.BindingFlags.CreateInstance, null, 
                        null, null,null,null );
    math = ( o.Unwrap() :> Math);
  }
}

As the comment says, for each method in IMath interface the implementation will be automatically created in a way similar to

public double Add( x : double, y : double ) {
  math.Add(x,y);
}

so we can then use MathProxy as

public class ProxyApp
{
  public static Main() :  void
  {
    // Create math proxy
    def  p =  MathProxy();

    // Do the math
    Console.WriteLine( "4 + 2 = {0}", p.Add( 4.0, 2.0 ) );
    Console.WriteLine( "4 - 2 = {0}", p.Sub( 4.0, 2.0 ) );
    Console.WriteLine( "4 * 2 = {0}", p.Mul( 4.0, 2.0 ) );
    Console.WriteLine( "4 / 2 = {0}", p.Div( 4.0, 2.0 ) );
  }
}

How we do this with a macro

In code above we used an attribute [DesignPatterns.Proxy (IMath)]. Actually it is a macro, which is invoked by compiler and will do what we need.

The implementation is presented below. It uses some of the compiler's API, but we will briefly describe what happens here.

using Nemerle.Compiler;
using Nemerle.Collections;

namespace DesignPatterns
{
  [Nemerle.MacroUsage (Nemerle.MacroPhase.WithTypedMembers,
                       Nemerle.MacroTargets.Field)]
  macro Proxy (t : TypeBuilder, f : FieldBuilder, iface)
  {
    // find out the real type specified as [iface] parameter
    def interfc = match (Nemerle.Macros.ImplicitCTX().BindType (iface))
    {
      | FixedType.Class (typeinfo, _) when typeinfo.IsInterface => typeinfo
      | _ => Message.FatalError ("expected interface type")
    }
    foreach (meth :> IMethod in interfc.GetMembers ())
    {
          // prepare interface method invocation arguments
          def parms = NList.Map (meth.GetParameters (), fun (p) {
            <[ $(t.ParsedName.NewName (p.name) : name) : $(p.ty : typed) ]>
          });
          // prepare function parameters of created method
          def fparms = NList.Map (parms, Parsetree.PParameter);

          // define the wrapper method
          t.Define (<[ decl:
            public virtual $(meth.Name : dyn) (..$fparms) : $(meth.ReturnType : typed) {
              this.$(f.Name : dyn).$(meth.Name : dyn) (..$parms)
            }
          ]>)
    } 
  }
}

Our macro takes as a parameter the name of interface, for which we need to create methods. This is just a plain expression, so we need to ask compiler what it really is and if it really describes an interface. We get an instance of Nemerle.Complier.TypeInfo this way.

Then we iterate over members of this interface type, casting them to IMethod interface. From this interface we can obtain all information needed about the method. Its name and parameters.

The process of generating new method's declaration is a little bit complex. We must separately create expressions describing its parameters and expressions for method invocation. Finally we create code for calling method on the field for which our macro was used.

Macro included in standard library

Since version 0.9.2 of Nemerle, the slightly modified version of Proxy macro has been included into standard macros library. It can be accessed via Nemerle.DesignPatterns.ProxyPublicMembers. It creates wrappers for public members like methods and properties from given field to current type.

So if you have a class

class Foo ['a] {
  public Length : int {
    get { .. } 
  }
  public Fire (x : int) : void {  }
  public Gene (x : 'a) : 'a { x }
}

you can automatically duplicate its methods in your class like

[Record]
class Bar {
  [Nemerle.DesignPatterns.ProxyPublicMembers ()]
  my_foo : Foo [int];
}

def bar = Bar (Foo ());
_ = bar.Length;
bar.Fire (1);
_ = bar.Gene (1);

You can use Include and Exclude parameters to generate proxies only for some set of members (see record macro for more detailed specification)

Singleton design pattern

Singleton Design Pattern ensures a class has only one instance and provide a global point of access to it. It lets you encapsulate and control the creation process by making sure that certain prerequisites are fulfilled or by creating the object lazily on demand.

What do we want to achieve?

We want to have a class, which will be automatically created when requested and then the created instance will be stored for any futher requests. We just want to specify the name of property by which we will access class' instance and write the needed logic. Management of lazy creation and storage should be hidden, because it does not introduce any valuable information.

using System;
using Nemerle.Collections;
using System.Threading;

// "Singleton"

[DesignPatterns.Singleton (GetLoadBalancer)]
class LoadBalancer
{
  private servers : Vector [string] = Vector (5);
  private random : Random = Random ();

  // Constructors (protected)
  protected this ()
  {
    // List of available servers
    servers.Add( "ServerI" );
    servers.Add( "ServerII" );
    servers.Add( "ServerIII" );
    servers.Add( "ServerIV" );
    servers.Add( "ServerV" );
  }

  // Properties
  public Server : string
  {
    get
    {
      // Simple, but effective random load balancer
      def r = random.Next (servers.Count);
      servers [r];
    }
  }
}

[DesignPatterns.Singleton (GetLoadBalancer)] attribute specifies that given class will be a singleton and that its instance will be accessed through property GetLoadBalancer.

public class SingletonApp
{
  public static Main() : void
  {
    def b1 = LoadBalancer.GetLoadBalancer;
    def b2 = LoadBalancer.GetLoadBalancer;
    def b3 = LoadBalancer.GetLoadBalancer;
    def b4 = LoadBalancer.GetLoadBalancer;

    // Same instance?
    when ((b1 : object == b2) && (b2 : object == b3) && (b3 : object == b4))
      Console.WriteLine( "Same instance" );

    // Do the load balancing
    Console.WriteLine( b1.Server );
    Console.WriteLine( b2.Server );
    Console.WriteLine( b3.Server );
    Console.WriteLine( b4.Server );
  }
}

How do we implement it in macro?

The task of a macro will be to

  • create a field for storing the only instance
  • create the property for accessing it, which will lazily call the constructor
  • make sure that there is only one constructor and make sure it is protected
using Nemerle.Compiler;
using Nemerle.Collections;

namespace DesignPatterns
{
  [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance,
                       Nemerle.MacroTargets.Class)]
  macro Singleton (t : TypeBuilder, getter)
  {
    def mems = t.GetParsedMembers();
    // find constructor, which we will need to call
    // to create instance
    def ctor = mems.Filter(fun (x) {
      | <[ decl: ..$_ this (..$_) $_ ]> => true
      | _ => false
    });
    match (ctor)
    {
      | [ <[ decl: ..$_ this (..$parms) $_ ]> as constructor ] =>
        match (getter)
        {
          | <[ $(getter_name : name) ]> =>
            // we must prepare expressions for invoking constructor
            def invoke_parms = parms.Map(x => <[ $(x.ParsedName : name) ]>);

            // first define the field, where a single instance will be stored
            t.Define(<[ decl:
              private static mutable instance : $(t.ParsedName : name);
            ]>);

            // finally, define getter
            t.Define (<[ decl:
              public static $(getter_name : name) : $(t.ParsedName : name)
              {
                get
                {
                  // lazy initialization in generated code
                  when (instance == null)
                    instance = $(t.ParsedName : name) (..$invoke_parms);
                  instance;
                }
              } 
            ]>);

            // make sure constructor is protected
            constructor.Attributes |= NemerleAttributes.Protected;

          | _ =>
            Message.FatalError ($"Singleton must be supplied with a simple name for getter, got $getter")
        }
      | _ => Message.Error ("Singleton design pattern requires exactly one constructor defined")
    } 
  }
}

First we search through members of the class and find all constructors. We make sure there is only one, read its parameters. After building expressions for invoking constructor we define needed class members. Finally constructor is marked as protected.

⚠️ **GitHub.com Fallback** ⚠️