Calling superclass method outside of redefined method

13 views (last 30 days)
I have an "DataAnalyzer" classdef that has a method that slowly computes exact solutions to certain queries.
classdef DataAnalyzer < HandleCompatibleSuperClass
methods
function obj = DataAnalyzer(dat,varargin)
obj = obj@HandleCompatibleSuperClass(varargin{:})
% lengthy constructor using dat with immutable properties
end
y_results = analyze(obj,x_queries) % slow analysis method
% lots of other user methods to analyze data
end
end
For debugging, we can get good enough results with a DataInterpolator, that provides all the same methods EXCEPT redefines the analyze method.
classdef DataInterpolator < DataAnalyzer
properties(SetAccess=immutable)
DomainX
RangeY
end
methods
function obj = DataInterpolator(x_domain,dat,varargin)
obj = obj@DataAnalyzer(dat,varargin{:});
obj.DomainX = x_domain;
obj.RangeY = analyze@DataAnalyzer(obj,x_domain);
% ^ Can't do this!
end
function y_results = analyze(obj,x_queries)
y_results = interp1(obj.DomainX,obj.RangeY,x_queries,'linear');
end
% all those other useful methods to analyze data
end
end
Subclass methods can call superclass methods if both methods have the same name.
I realize that I could do something like this...
classdef DataInterpolator < DataAnalyzer
properties(SetAccess=immutable)
DomainX
RangeY
end
methods
function obj = DataInterpolator(x_domain,dat,varargin)
obj = obj@DataAnalyzer(dat,varargin{:});
% Create a temp object, but time is a penalty.
temp_obj = DataAnalyzer(dat,varargin{:});
obj.DomainX = x_domain;
obj.DomainY = analyze(temp_obj,x_domain);
end
function y_results = analyze(obj,x_queries)
y_results = interp1(obj.DomainX,obj.RangeY,x_queries,'linear');
end
% all those other useful methods to analyze data
end
end
But that feels like a really ugly hack, even for debug. Worse, I lose the performance benefits of constructing obj essentially twice.
Is there a smarter way to accomplish what I'd like to do here?

Accepted Answer

Jacob Lynch August
Jacob Lynch August on 16 Feb 2021
Edited: Jacob Lynch August on 16 Feb 2021
I went back to the author and brought up these techniques. He agrees that the level of hackery is unforuntate, but this is what we agreed to:
  1. An analyze_protected_static method was added to let us access any superclass analyze method.
  2. The constructor of Interpolator calls that static method of the superclass to get the instance data.
classdef DataAnalyzer < HandleCompatibleSuperClass
methods
function obj = DataAnalyzer(dat,varargin)
obj = obj@HandleCompatibleSuperClass(varargin{:})
% ... lengthy constructor using dat to set immutable properties.
end
% This method is just a wrapper for the protected, static method.
y_results = analyze(obj,x_queries)
% ... lots of other user methods to analyze data
end
methods(Static,Access=protected)
% The true, slow analysis method.
y_results = analyze_protected_static(obj,x_queries)
end
end
Then the Interpolator subclass just constructs the domain data from the static method.
classdef DataInterpolator < DataAnalyzer
properties(SetAccess=immutable,GetAccess=private)
DomainX (:,1)
RangeY (:,:)
end
methods
function obj = DataInterpolator(x_domain,dat,varargin)
% Construct object
obj = obj@DataAnalyzer(dat,varargin{:});
obj.DomainX = x_domain;
obj.RangeY = DataAnalyzer.analyze_protected_static(obj,obj.DomainX);
end
function y_results = analyze(obj,x_queries)
% No if cases; YAY!
y_results = interp1(obj.DomainX,obj.RangeY,x_queries,'linear',nan);
end
end
end
Thank you for all your advice. It helped when discussing this with the author. It's unfortunate that we'll have to redistribute the pcode to other members that are using these methods.

More Answers (2)

Matt J
Matt J on 15 Feb 2021
Edited: Matt J on 15 Feb 2021
Why do you have to do it through inheritance? Couldn't you pass a function handle to the analyze() method that you want as a constructor argument?
classdef DataAnalyzer < HandleCompatibleSuperClass
properties(SetAccess=immutable)
DomainX
RangeY
analyzeMethod; %handle to analyze() method
end
methods
function obj = DataAnalyzer(dat,analyzeMethod,varargin)
obj = obj@HandleCompatibleSuperClass(dat,varargin{:})
obj.RangeY=analyzeMethod(obj.DomainX);
obj.analyzeMethod=analyzeMethod; %store it to a property for later use, if desired.
end
end
end
  1 Comment
Jacob Lynch August
Jacob Lynch August on 16 Feb 2021
  1. To maintain intellectual property, much of this is distributed as p-code, even internally, so editing that isn't feasible. (I know pcode is simply obfuscated, not encrypted.)
  2. Approximator has all the same properties as Analyzer, so should could compute all the same data when using the same method. Inheritance feels like the shortest path to keep all the other functionality of Analyzer, but just employing a shortcut on analyze().
  3. I've been bitten too many times by global and persistent variables to ever trust them, even when I think I'm being clever by time stamping the instantiated object.

Sign in to comment.


Matt J
Matt J on 16 Feb 2021
Edited: Matt J on 16 Feb 2021
It should be enough just to provide an overriding analyze() method in the subclass,
classdef DataApproximator < DataAnalyzer
methods
function obj = DataApproximator(dat,varargin)
obj = obj@DataAnalyzer(dat,varargin{:});
end
function y_results = analyze(obj,x_queries)
y_results = interp1(obj.DomainX,obj.RangeY,x_queries,'linear');
end
% all those other useful methods to analyze data
end
end
  3 Comments
Jacob Lynch August
Jacob Lynch August on 16 Feb 2021
Edited: Jacob Lynch August on 16 Feb 2021
My workaround to let others prototype with the quicker debug Interpolator class, is check if the Doman and Range data fields are empty.
classdef DataInterpolator < DataAnalyzer
properties(SetAccess=immutable,GetAccess=private)
DomainX (:,1)
RangeY (:,:)
end
methods
function obj = DataInterpolator(x_domain,dat,varargin)
% Construct object
obj = obj@DataAnalyzer(dat,varargin{:});
obj.DomainX = x_domain;
obj.RangeY = analyze(obj,obj.DomainX);
end
function y_results = analyze(obj,x_queries)
if isempty(obj.RangeY)
y_results = analyze@DataAnalyzer(obj,x_queries);
else
y_results = interp1(obj.DomainX,obj.RangeY,'linear',nan);
end
end
end
end
I'm not proud of this approach, as it breaks my coding asthetics to "never use an if statement for something that will only happen once."
Matt J
Matt J on 16 Feb 2021
Edited: Matt J on 16 Feb 2021
I'm not proud of this approach.
I think it's pretty good, although I would probably refine it as below. I think some degree of hackery will be inevitable, since if the implementation of the superclass is uneditable, you are blocked from doing things the "right way".
classdef DataApproximator < DataAnalyzer
properties(SetAccess=immutable,GetAccess=private)
DomainX (:,1)
RangeY (:,:)
end
methods
function obj = DataApproximator(x_domain,dat,varargin)
% Construct object
obj = obj@DataAnalyzer(dat,varargin{:});
obj.DomainX = x_domain;
obj.RangeY = analyze(obj,'superclass');
end
function y_results = analyze(obj,varargin)
normalOperation = ~(nargin>1 && strcmp(varargin{1},'superclass') );
if normalOperation
y_results = interp1(obj.DomainX,obj.RangeY,'linear',nan);
else
y_results = analyze@DataAnalyzer(obj,obj.DomainX);
end
end
end
end

Sign in to comment.

Categories

Find more on Class File Organization in Help Center and File Exchange

Products


Release

R2020b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!