Is this Feature Supported?


Quite often new features are added into a code stream for demonstration purposes, or need to be hidden because they are not quite ready for release (especially true for monolithic EXEs), or the client cannot upgrade their database structures yet.

There usually are various home grown approaches to accomplish this ranging from command line parameters (beta switches to enable or disable functionality), to one off database metadata queries.  These approaches can be unified in an interface added to your database “data session” as shown in the following code snippets.


type
  TFeatureSupportedPredicate = reference to function(DataSession :IDataSession): Boolean;
  TFeatureInfo = class(TObject)
    Supported :integer;
    FeatureSupportedPredicate :TFeatureSupportedPredicate;
  public
    constructor Create;
  end;

  constructor TFeatureInfo.Create;
  begin
    inherited;
    Supported := -1;  //used to indicate the test for existence has not been performed
  end

type
  TDataSession  = class(TInterfacedObject,IDataSession)
  var
    FFeatureSupportDictionary :TDictionary<string,TFeatureInfo>;
    constructor Create; override;
  end;


  constructor  TDataSession.Create;
  begin
    //do some stuff
    FFeatureSupportDictionary := TDictionary<string,TFeatureInfo>.Create;
  end;

procedure TDataSession.AddFeatureSupportPredicate(
            const FeatureName :string;
            Predicate :TFeatureSupportedPredicate);
var
  FeatureInfo :TFeatureInfo;
begin
  if FFeatureSupportDictionary.ContainsKey(FeatureName) then
    raise EFeatureSupportException.
                      CreateFmt('A Feature Support Predicate is already '+
                                'registered with the Name: ''%s'' ',[FeatureName]);
  FeatureInfo := TFeatureInfo.Create;
  FeatureInfo.FeatureSupportedPredicate := Predicate;
  FFeatureSupportDictionary.Add(FeatureName,FeatureInfo);
end;

function TDataSession.IsFeatureSupported(const FeatureName :string) :boolean;
var
  FeatureInfo :TFeatureInfo;
begin
  Result := False;
  if FFeatureSupportDictionary.TryGetValue(FeatureName,FeatureInfo) then
  begin
    if FeatureInfo.Supported = -1 then
      FeatureInfo.Supported := ifthen(FeatureInfo.FeatureSupportedPredicate(Self),1,0);
    Result := Boolean(FeatureInfo.Supported);
  end;
end;



Now wherever you pass or inject your IDataSession interface, which in a Client/Server application is almost everywhere, you will have access to the FeatureSupport functionality, and you can add/test predicates with the following code snippets:

  AddFeatureSupportPredicate(SomeFeature,
    function(DataSession: IDataSession): Boolean
    begin
      Result := DataSession.TableExists('SomeTableForTheFeature');
    end);

if DataSession.IsFeatureSupported(SomeFeature) then ....

2 Responses to “Is this Feature Supported?”

  1. Stefan Glienke Says:

    IMO a cleaner approach for optional features is using design patterns like decorator or chain of responsibility instead of if checks. This does not only make the code cleaner but separate the different concerns into their own components/classes.

  2. Larry Hengen Says:

    @Stefan,

    I failed to mention that I used these changes in a 25 year old C/S application to consolidate a lot of one off SQL calls, often done repeatedly in various places to establish if the same columns were present in the database. Obviously the apps in question were not very object oriented in design and have lots of form code. This is typical of Delphi legacy code. Instead of a massive re-factoring, I chose to implement a testable class at this seam (the code must support Oracle and SQL), consolidating all the similar code scattered throughout the app. While I could see applying design patterns if this were a ground up implementation, I don’t think they apply in this particular situation.

Leave a Reply