Dynamic Methods in Static-Typed Languages

One of the great benefits of dynamic-typed languages is that they are able to add functionality to an object on run-time. For example, you can create an object and then add functions to it, giving it the desired behavior, without a complex inheritance structure. Moreover, when calling the object’s methods, the client can determine whether the object supports a certain operation or not.

Static-typed languages do not come with this advantage. Creating objects that share some behavior can be done by inheritance, yet this restricts the combinations of behaviors an object can have (unless it relies on multiple-inheritance, which has some tricky effects).

Instead of method calls (inspired from Smalltalk), Objective-C implements a system of “message-passing” in object instances. This allows a client to call a method that might not exist on an object, without throwing any compile-time or run-time errors. The object can respond to the message, if it implements the behavior.

In this post, we will describe a similar behavior of safely calling methods on abstract objects that might or might not implement them.

The Problem

You have multiple models and multiple behaviors that relate N-M. Each model contains a subset or all the behaviors in a way that cannot be achieved without multiple inheritance. This is because models share some behaviors, but there is no “is-a” relationship between them. Behaviors do not have a common interface and each takes different arguments. Also, the models might not have a static structure.

The standard Decorator pattern cannot be used, as it requires decorators to share the same interface and to have the same interface as the model’s. Also, they cannot be called individually.

The client component wants to perform operations on the models, but doesn’t know any specifics of the models or the behaviors they support (calls are based on user-input; errors, such as a model that does not support a certain operation, are acceptable).

draft-pattern-attribute-decorator-n-n-diagram
 

Example

You have a HTTP API Server that exposes various resources using REST and JSON. Each of these resources have several properties of different types. Some of these properties can contain other properties or even other resources. Also, these resources support certain operations, not only CRUD operations. Some operations are shared by most resources.

The resource that gets manipulated and the operation that gets called is determined when parsing the request. Also, the combination might not always be valid.

The Front Controller cannot know about all the resources. So it might use a factory, but this limits its interaction to the abstract interface of the resources and cannot access its specific operations. An additional controller might be used for every resource, but this increases complexity and duplicates the code.

What we need:

  • To share the behavior code between several resources, preferably without duplicating the code
  • A document-structure for the resources
  • A way to determine whether a certain resource supports a certain behavior or not
  • A different interface and different parameters for each behavior
  • A simple adapter from REST to resources, without complicated resource-specific controllers.

Solution

Instead of Inheritance, we will extend the behavior of objects through Composition. We will have a class for each behavior and the objects will aggregate these classes. Each behavior can contain multiple operations that will be called by name.

This allows us to dynamically place any combination of methods on objects. At this point, the behavior of objects can also be built at run-time, without creating new classes.
 
draft-pattern-attribute-decorator
 
The behavior can be added to the Object:

  • From the object’s constructor or from the constructor of an object’s child
  • From a Builder. The Builder will act like a factory for the object.
  • Dynamically, from the client (by implementing some methods on the object for Behavior management).

Example

draft-pattern-attribute-decorator-example-2
 

Code Snippets

Dynamic Language (e.g. Javascript)


/* Our ResourceX "class" "constructor" */
foo.ResourceX = function() {

   /* Set the main properties of the object; refer to above diagram */
   this.name = "ResourceX";
   this.identifier = null; /* none yet */
   this.data = {}; /* map */

   /* Add some structure to our "resource" */
   /* Set the "JSON" properties; refer to above diagram */
   this.data['name'] = new foo.String();
   this.data['otherfield'] = new foo.String();
   this.data['list'] = new foo.Array();

   /* Add behavior */
   /* We assign some "static" pre-created functions to the object */
   /* Notice that we consider our ResourceX not to support "Update" */
   this.CRUDCreate = foo.ResourceMethods.CRUDCreate;
   this.CRUDRead = foo.ResourceMethods.CRUDRead;
   this.CRUDDelete = foo.ResourceMethods.CRUDDelete;

   /* Our ResourceX instance is built */
   return this;
};

/* The client */
foo.Client.exampleHTTPPost = function(json_data) {
   var resource = new ResourceX();
   if (resource.CRUDCreate) {
      return resource.CRUDCreate(json_data);
   } else {
      throw new OurHTTPServerError(405, METHOD_NOT_ALLOWED);
   }
};

Static-Typed Language (e.g. C++)


/* Our ResourceX class constructor */
ResourceX::ResourceX() {
   /* Set the main properties of the object; refer to above diagram */
   this->name = "ResourceX";
   this->identifier = null;
   
   /* Add some structure to our "resource" */
   /* Set the "JSON" properties; refer to above diagram */
   this->data["name"] = new foo::String();
   this->data["otherfield"] = new foo::String();
   this->data["list"] = new foo::Array();

   /* Add behavior */
   /* Notice that we consider our ResourceX not to support "Update" */
   this->_addBehavior(new foo::CRUDCreate());
   this->_addBehavior(new foo::CRUDRead());
   this->_addBehavior(new foo::CRUDDelete());
}

/* The Client */
std::string Client::exampleHTTPPost(const std::string& json_data) {
   JSONResource *resource = new ResourceX();
   try {
      std::vector args;
      args.push(json_data);
      return resource->executeOperation("create", args);
   }
   catch (AbstractBehavior::Exception& e) {
      throw new OurHTTPServerError(405, METHOD_NOT_ALLOWED);
   }
}

Further Reading

The programming language is just a tool. Before writing the code, you must carefully design and choose the proper solution. This will save a lot of coding time and will enhance quality. You can read more about the Visitor family of design patterns here. You will find detailed information about the Extension Object pattern shortly introduced in this article.

We also advise you to read this article about the static and dynamic type.

Post A Reply