Flexible Method Pattern

This is the first in a series of posts on some of the coding patterns that I have been using with Peso.  I use the term 'pattern' loosely, referring to common code patterns that are repeated through out the implementation of the framework.

The intent of the Flexible Method Pattern is to give callers of the framework functions/methods as much flexibility as possible, minimise the amount of boiler plate code required to use the framework, and keep the code as streamlined and simple as possible.  This pattern is made up of two parts.

Type Specific Handling

Often we might need to have the same logical operation being performed, pretty much the same way, for two or more different types corresponding to the same logical input.

For example, a common operation is to retrieve an element given an ElementID or an ElementGUID.  There are two different methods on the Repository object to do this.  In the framework we can do a bit better than this.  We can write a method that takes an 'identifier' and depending on the type can determine which underling method to call.  Below is an example of the code, and how it could be used.

!INC Cloudy Models Blog.common

const TST_ELM_GUID = '{6BEB9478-087A-498b-A80D-1408D123471F}';
const TST_ELM_ID = 909;

function getElement(id) {
    if (typeof id == 'string') {
        return Repository.GetElementByGuid(id);
    }
    else if (typeof id == 'number') {
        return Repository.GetElementByID(id);
    }
    return null; // or throw exception
}

var elm1 = getElement(TST_ELM_GUID);
var elm2 = getElement(TST_ELM_ID);

dbg('id = ' + elm1.ElementID + ', guid = ' + elm1.ElementGUID);
dbg('id = ' + elm2.ElementID + ', guid = ' + elm1.ElementGUID);

Destructuring Optional Options Parameter

Often we have methods that can execute for most cases with some default behaviour, while only wanting slightly different behaviour in a small number of cases.  The usual way to do this is to take advantage of Javascript's variable function arguments.  That is we can check for defined parameters, use default parameter values or use the 'Arguments' object.  See here for a description on each of these options.

Each of the above work fine if you only have one option or flag, but it becomes awkward for multiple of these.  In which case, the caller has to provide the default values for the options that appear sequentially before the wanted ones.  See example below for an illustration of this issue.

function reportOnElementV1(elm, includeName=false, includeAlias=false) {
 var report = 'id = ' + elm.ElementID + ', ';
 if (includeName) {
  report += 'name = ' + elm.Name + ', ';
 }
 if (includeAlias) {
  report += 'alias = ' + elm.Alias + ', ';
 }
 return report;
}

var reportV1 = 'ReportV1: ' + reportOnElementV1(elm1, false, true);
dbg(reportV1);

A workaround for the above problem is to provide an optional options object and have the function check for the properties within the options object.  See example below.

function reportOnElementV2(elm, options) {

 var includeName = false;
 var includeAlias = false;
 if (options) {
  if (typeof options.includeName != 'undefined') {
   includeName = options.includeName;
  }
  if (typeof options.includeAlias != 'undefined') {
   includeAlias = options.includeAlias;
  }
 }

 var report = 'id = ' + elm.ElementID + ', ';
 if (includeName) {
  report += 'name = ' + elm.Name + ', ';
 }
 if (includeAlias) {
  report += 'alias = ' + elm.Alias + ', ';
 }
 return report;
}

var reportV2 = 'ReportV2: ' + reportOnElementV2(elm1, {includeAlias: true});
dbg(reportV2);

Short forms of the 'if' statement checking the options exist, however imho this clutters the code and adds a significant amount of boiler plate code.

With ECMA2015 and Destructuring_Assignment we can avoid the options handling clutter and focus on what the operation should be doing instead.  The following is an example of what is used throughout the framework.

function reportOnElementV3(elm, {includeName=false, includeAlias=false}={}) {

    var report = 'id = ' + elm.ElementID + ', ';
    if (includeName) {
        report += 'name = ' + elm.Name + ', ';
    }
    if (includeAlias) {
        report += 'alias = ' + elm.Alias + ', ';
   }
    return report;
}

var reportV3 = 'ReportV3: ' + reportOnElementV3(elm1, {includeAlias: true});
var reportV4 = 'ReportV4: ' + reportOnElementV3(elm1);

dbg(reportV3);
dbg(reportV4);

Note the '{}' in the destructuring statement.  This is required to take care of cases where no options are provided at all, which illustrated by reportV4.

[Originally posted here.]

Comments

Popular posts from this blog

Creating Items in Sparx EA via Java

Module Management - Part 1 - getModule() Pattern

Traversing a EA Model via Java