|
"Sven" <sven.holcombe@gmail.deleteme.com> wrote in message <hg8nks$nco$1@fred.mathworks.com>...
> Hi all,
>
> I have the following dilemma. I'm writing an object to handle my database interaction. I am using the class property Data to store a structure of column names / value pairs. At the same time, I want to track *which* of those fields in Data have been updated.
>
> To illustrate the example, imagine I have an object such as:
>
> classdef DbObject
> properties
> Data = struct();
> potentialFields = [];
> updatedFieldIdxs = []; % Idxs into potentialFields for updated field
> end
> end
>
> I now want to do the following:
>
> myTable = DbObject; % Create the database object
> myTable.potentialFields = {'field1';'field2'}; % Tell it what fields to expect
> myTable.Data.field1 = 400; % Set one of those fields
>
> Now, at this point, I have updated 'field1'. I would like the updatedFieldIdxs property to contain a vector telling me which of the potentialFields have been updated. Ie, it should contain the vector [1 0]. This will help me down the line when I want to save the object to a database: at that time I can send the minimal amount of info - only the updated fields.
>
> To me, it seems this is an ideal situation to overload the set() method as follows:
> methods
> function this = set.Data(this,newData)
> % When Data is update, set the updated/deletedFieldIdxs vectors
> oldFieldNames = fieldnames(this.Data);
> newFieldNames = fieldnames(newData);
> addedFieldNames = setdiff(newFieldNames, oldFieldNames);
> sharedFields = intersect(fieldnames(newData),oldFieldNames);
> changedFieldIdxs = false(1,length(sharedFields));
> for i = 1:length(sharedFields)
> if ~isequal(newData.(sharedFields{i}), this.Data.(sharedFields{i}))
> changedFieldIdxs(i) = true;
> end
> end
> % Finalise a list of modified or updated field names
> updatedFieldNames = sharedFields(changedFieldIdxs);
> updatedFieldNames = cat(1,updatedFieldNames,addedFieldNames);
>
> % Initialise property if empty
> if isempty(this.updatedFieldIdxs)
> this.updatedFieldIdxs = false(size(this.potentialFields,1),1);
> end
>
> % Set to true any fields that have been updated
> [~, idxs] = intersect(this.potentialFields(:,1), updatedFieldNames);
> this.updatedFieldIdxs(idxs) = true;
> % UPDATE THE DATA STRUCT!
> this.Data = newData;
> end
> end
>
> Now I've done this, and as far as I can tell, the implementation works. However, m-lint warns me that:
> "A set method for a non-dependent property should not access another property."
>
> It seems that the best time for me to compare the current contents of Data with the new contents of Data, and then updating the updatedFieldIdxs property *is* inside the Data.set() method. Another way of saying it is that my updatedFieldIdxs is dependent on the *comparison* of old Data vs. new Data, rather than simply being dependent on the Data property.
>
> So my question is: should I simply ignore the m-lint warning? Is there a better implementation I can follow?
==================
If you're careful, there's probably not going to be any problem, but it seems safer to update updatedFieldIdxs in an overloaded subsref() method (which overloads the dot indexing syntax) as opposed to set.Data
The problem with your approach is that set/get methods are called every time a property is referenced even INSIDE THE DEFINITION of the class. This is not true for subsref methods.
This means that when you use set.Data to change the property updatedFieldIdxs, the set method for updatedFieldIdxs is called. In this case, you haven't defined set.updateFieldIdxs, so MATLAB supplies a default definition, but what if you decided to do so later on, and what if your version of set.updateFieldIdxs accidentally calls set.Data. Well, then the two set methods would call each other in a never-ending loop.
I think this is what M-lint is trying to steer you away from. I could be wrong.
|