Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

Common Design Considerations for Object Properties

I’m pleased to introduce guest blogger Dave Foti. Dave has worked on MATLAB for over 15 years and currently manages the group responsible for object-oriented programming features in MATLAB. He is interested in giving MATLAB users the tools to meet increasingly complex challenges. This post will take a look at making use of object properties in MATLAB.

Contents

Property Basics

Properties are how MATLAB objects store data. At their most basic level, properties provide a way to define what data are required for a particular kind of object. For example, if I have a set of measurements and each measurement has the same pieces of information, then I can create a class of measurement objects with a property to store each piece of information as in:

type surfaceTempReading1
classdef surfaceTempReading1
  properties
    Date
    Latitude
    Longitude
    Temperature
  end
end

I might have an instance that looks like this:

t1 = surfaceTempReading1;
t1.Date = date;
t1.Latitude  = '42:16:4 N';
t1.Longitude = '71:20:2 W';
t1.Temperature = 260;
disp(t1);
  surfaceTempReading1

  Properties:
           Date: '16-Mar-2011'
       Latitude: '42:16:4 N'
      Longitude: '71:20:2 W'
    Temperature: 260

The MATLAB documentation describes properties here.

Dependent Properties

In simple cases like this, it is probably fine to have all properties directly store and retrieve data. However, MATLAB provides ways to define properties that don’t directly store data. These dependent properties are defined using the Dependent attribute as in:

type surfaceTempReading2
classdef surfaceTempReading2
  properties
    Date
    Latitude
    Longitude
    Temperature
  end

  properties(Dependent)
    Altitude
  end  

  methods
    function A = get.Altitude(reading)
      A = lookupAltitude(reading.Latitude, reading.Longitude);
    end
  end
end

In this case, if I have a data set of surface temperature readings, I just need latitude and longitude to identify where the temperature was sampled. However, in analyzing the data, it might be helpful to correlate temperatures with surface altitude. Rather than have to input this data, the altitude can be calculated from a database of Earth surface topography. Assume I have a function that can provide such a value called lookupAltitude, then I can invoke that function from the Dependent property called Altitude.

t2 = surfaceTempReading2;
t2.Date = date;
t2.Latitude  = '42:16:4 N';
t2.Longitude = '71:20:2 W';
t2.Temperature = 260;
disp(t2)
  surfaceTempReading2

  Properties:
           Date: '16-Mar-2011'
       Latitude: '42:16:4 N'
      Longitude: '71:20:2 W'
    Temperature: 260
       Altitude: 342

Changing Property Names

Dependent properties allow me some flexibility to implement the same external interface in different ways. A user of our class doesn’t necessarily need to know how altitude was determined or whether or not it is actually stored in a class instance. This flexibility can be useful in evolving a class over time while not breaking scripts and functions that might use the class. For example, if I decide that TimeStamp is a better name for the Date property, I can gradually start switching over to the new name without immediately changing all of the code I have using Date:

type surfaceTempReading3
classdef surfaceTempReading3
  properties
    TimeStamp
    Latitude
    Longitude
    Temperature
  end
  
  % Deprecated property names
  properties(Dependent, Hidden) 
    Date
  end

  methods
    function D = get.Date(reading)  
      D = reading.TimeStamp;
    end

    function reading = set.Date(reading, D)
      reading.TimeStamp = D;
    end
  end
end

When I change my class as above, not only will old scripts using Date continue to work, but old MAT-Files saved using the old class definition will automatically load the Date value into the new TimeStamp property. I use the Hidden attribute so that the old property name doesn’t show up in object display, the properties command, or tab completion.

t3 = surfaceTempReading3;
t3.Date = date;
disp(t3);
  surfaceTempReading3

  Properties:
      TimeStamp: '16-Mar-2011'
       Latitude: []
      Longitude: []
    Temperature: []

Side Effects and Validation

Dependent properties can also be useful any time a property has side effects – when for example changing a property triggers an update to some other object or graphic or when getting a property requires doing some expensive operation to acquire or validate the result. Often these expensive operations can be avoided from certain "trusted" code defined inside the class. Having a one private property that doesn’t perform any side effects allows the trusted code to work on the raw property value. Other code inside and outside the class can use a public and dependent property that performs the side effects or expensive operations. Sometimes the expensive operation is just validating the input value being assigned to the property. For example, in our class if I want to validate temperature values, I can do something like the following:

type surfaceTempReading4;
classdef surfaceTempReading4
  properties
    Date
    Latitude
    Longitude
  end
  properties (Dependent)
    Temperature
  end
  
  properties(Access=private)
    pTemperature
  end

  methods
    function reading = set.Temperature(reading, t)
      if t < 0 
        error('SurfaceTemp:BadTemp', ...
              'Temperature must be a positive number in Kelvins.');
      end
      reading.pTemperature = t;
    end
    function t = get.Temperature(reading)
        t = reading.pTemperature;
    end
  end
end

NOTE: The above example has been changed to correct an error in the original post.

t4 = surfaceTempReading4;
try
    t4.Temperature = -15;
catch err
    disp(err.message);
end
Temperature must be a positive number in Kelvins.

Default Values

Classes can define default values for properties. A default value is defined by the class and each instance of the class is assigned that default value. Default values can be used when there is a constant default that can be documented with the class definition. Default values can save space in MAT-Files because defaults are saved once per class and values of properties not differing from default values don’t need to be saved. Generally speaking default values should be literal values wherever possible so that it is clear what the default is. For example:

type surfaceTempReading5
classdef surfaceTempReading5
  properties
    Date
    Latitude = '0N';
    Longitude = '0E';
    Temperature = 0;
  end
end

While it is possible to use the result of a function for a default value, one should avoid doing so unless the function returns a constant value. For example, it might be tempting to do something like:

type surfaceTempReading6
classdef surfaceTempReading6
  properties
    % Get the current date from MATLAB
    Date = date; 
    
    Latitude
    Longitude
    Temperature
  end
end

However, the problem with this is that the current date is not a constant default value that can be documented as part of the class. Moreover, since MATLAB evaluates default property expressions in class definitions only when a class is loaded or reloaded, the function call will generally happen once in a MATLAB session. Each object will get the same date stamp that was current when the class was loaded and not the current date at the time an object is created (perhaps the next day). Generally, one doesn’t want to assign a default value to a handle for a similar reason. If the intent is to make a new handle for each instance, then this handle has to be created in the constructor and not as a default value. Since defaults are the same for all instances created from a given class definition, all objects would get the same handle. For more information on handle classes, see Handle and Value Classes.

I’ve described a few uses for properties and a few common attributes, but I would be very interested in hearing how you use properties. You can post ideas here.




Published with MATLAB® 7.11


  • print