Search code examples
matlaboopdictionarysingletonhandle

Singleton registry class


I would like to create a singleton MATLAB class acting as a global registry. The registry should store objects (of a certain class derived from handle) addressed with unique names. I would like to access the properties of the stored classes conveniently without temporary variables, for example:

Registry.instance().addElement('name1', NewObject(...));
Registry.instance().get('name1').Value
Registry.instance().get('name2').Value = 1;

Reading out properties of the returned class can be circumvented by removing the () from instance:

 >> Equipment.instance.get('name1').Value

However, it does not seem easy to use assignments because as noted in the comments, dot-indexing can't be used directly on the output of a function without assigning to an intermediate variable.

What is the proper way to implement and use such a "singleton registry" in MATLAB?

It should be noted that the singleton class contains some logic which is called when adding elements to the list, logic to properly destroy the objects in the right order and other methods which iterate through the object list. For that reason, a "normal" containers.Map cannot be used.


Solution

  • This might be what you're looking for:

    classdef (Abstract) Registry % < handle   <-- optional
    
      methods (Access = public, Static = true)
    
        function addElement(elementName, element)
          Registry.accessRegistry('set', elementName, element );
        end    
    
        function element = get(elementName)
          element = Registry.accessRegistry('get', elementName);
        end
    
        function reset()
          Registry.accessRegistry('reset');
        end
      end
    
      methods (Access = private, Static = true)
    
        function varargout = accessRegistry(action, fieldName, fieldValue)
        % throws MATLAB:Containers:Map:NoKey
          persistent elem;
          %% Initialize registry:
          if ~isa(elem, 'containers.Map') % meaning elem == []
            elem = containers.Map;
          end
          %% Process action:
          switch action
            case 'set'
              elem(fieldName) = fieldValue;
            case 'get'
              varargout{1} = elem(fieldName);
            case 'reset'
              elem = containers.Map;
          end        
        end
      end
    
    end
    

    Since MATLAB doesn't support static properties, one must resort to various workarounds, possibly involving methods with persistent variables, as is the case in my answer.

    Here's a usage example of the above:

    Registry.addElement('name1', gobjects(1));
    Registry.addElement('name2', cell(1) );     % assign heterogeneous types
    
    Registry.get('name1')
    ans = 
      GraphicsPlaceholder with no properties.
    
    Registry.get('name1').get    % dot-access the output w/o intermediate assignment
      struct with no fields.
    
    Registry.get('name2'){1}     % {}-access the output w/o intermediate assignment
    ans =
         []
    
    Registry.get('name3')        % request an invalid value
    Error using containers.Map/subsref
    The specified key is not present in this container.
    Error in Registry/accessRegistry (line 31)
              varargout{1} = elem(fieldName);
    Error in Registry.get (line 10)
          element = Registry.accessRegistry('get', elementName);