How can I define a class constant based on an abstract constant?

35 views (last 30 days)
I am trying to write an abstract class with constant properties. One of these constant properties depends on the others, but will be computed the same way for each sub-class.
Here is a simple contrived minimum example of what I am trying to achieve:
classdef (Abstract) BaseData
properties (Abstract = true, Constant = true)
data;
end
properties (Constant = true)
avg = mean(BaseData.data); % Doesn't work as desired
end
properties
% ...
end
methods
% ...
end
end
classdef TestData < BaseData
properties (Constant = true)
data = [1, 2, 3, 4, 5];
end
end
In this example I would like avg to be computed and saved as a class constant for each sub-class I write.
Some solutions I have thought of:
(1) The above
avg is empty because BaseData.data is empty when the command is run.
(2) Compute avg in each sub-class
I could do this, but it is duplicate code.
(3) Store avg as an immutable property and compute it in the constructor for the class
This solution is not quite what I want either. For every TestData object I make, the exact same computation is performed to get the exact same result each time. In addition, the property is stored on a per-object basis instead of at class scope.
  1 Comment
Jacob Lynch August
Jacob Lynch August on 10 Aug 2021
A good solution is one that does not obfuscate the codes' structure or intention, and the two workarounds (replicate code in each subclass, calculate on construct) unforuntately do just that.
A third, bad workaround would memoize results.
classdef (Abstract) AbstractClass
properties(Abstract,Constant)
data
end
properties(Dependent)
avg
end
methods
function m = get.avg(obj)
f = memoize(@mean);
m = f(obj.data);
end
end
end
classdef DefiniteClass < AbstractClass
properties(Constant)
data = 1:5;
end
end
I wish there was a better way to accomplish this, as I have large data that should be loaded/processed only once, and each subclass specifies what is that large block of data and how it is processed.

Sign in to comment.

Accepted Answer

per isakson
per isakson on 29 Apr 2020
Edited: per isakson on 30 Apr 2020
Doesn't this meet your requirements? The property, data, doesn't need Abstract=true and it would force you to define data in all subclasses.
>> td = TestData
td =
TestData with properties:
data: [1 2 3 4 5]
avg: 3
>> bd = BaseData
Abstract classes cannot be instantiated. Class 'BaseData' is declared as Abstract.
>> td.data = [6,7,8,9,0]
You cannot set the read-only property 'data' of TestData.
>>
where
classdef (Abstract) BaseData
properties (Constant = true)
data = [1, 2, 3, 4, 5];
avg = mean(BaseData.data);
end
end
and
classdef TestData < BaseData
end
In responce to comment
"all [instances of TestData shall] have different constant data" raises the question how to provide this unique value to the specific instance. As input to the constructor, I would say.
>> td = TestData([1:5])
td =
TestData with properties:
data: [1 2 3 4 5]
avg: 3
>> td2 = TestData([6:10])
td2 =
TestData with properties:
data: [6 7 8 9 10]
avg: 8
>> td.data = [6,7,8,9,0]
You cannot set the read-only property 'data' of TestData.
>>
where
classdef (Abstract) BaseData
properties ( SetAccess = {?TestData} )
data
end
properties ( Dependent = true )
avg
end
methods
function m = get.avg( this )
m = mean( this.data );
end
end
end
and
classdef TestData < BaseData
methods
function this = TestData( data_value )
this.data = data_value;
end
end
end
"all [subclasses of BaseData shall] have different constant data"
Do my suggestions converge?
>> td1 = TestData_1
td1 =
TestData_1 with properties:
data: [0 1 2 3 4]
avg: 2
>> td1.data = 17;
You cannot set the read-only property 'data' of TestData_1.
>>
where
classdef (Abstract) BaseData_2
properties ( Abstract = true, Constant = true )
data
end
properties ( Dependent = true )
avg
end
methods
function m = get.avg( this )
m = mean( this.data );
end
end
end
classdef TestData_2 < BaseData_2
properties ( Constant = true )
data = (5:9);
end
end
classdef TestData_1 < BaseData_2
properties ( Constant = true )
data = [0,1,2,3,4];
end
end
And to avoid executing mean() for every access of avg, replace BaseData by
classdef (Abstract) BaseData_speed
properties ( Abstract = true, Constant = true )
data
end
properties ( Access = private )
hidden_avg
end
properties ( Dependent = true )
avg
end
methods
function this = BaseData_speed
this.hidden_avg = mean( this.data );
end
function val = get.avg( this )
val = this.hidden_avg;
end
end
end
  3 Comments
per isakson
per isakson on 30 Apr 2020
Edited: per isakson on 30 Apr 2020
"all [instances of TestData shall] have different constant data" raises the question how to provide this unique value to the specific instance. As input to the constructor, I would say. There is no way to pass that value from the subclass constructor to the superclass properties block. And if there was a way, that would only work for the first instance of the subclass. I bet that the tech support would tell you to make avg a dependent property, and that you thinks that will consume too many cpu-cycles.
classdef TestData < BaseData
methods
function this = TestData( data_value )
this.data = data_value;
end
end
end
Thus the answer is: Impossible!
(See: new code in the answer.)
Jacob Green
Jacob Green on 3 May 2020
Thanks for the answers. The lastest bit of code is the closest to what I would like to achieve. I believe every instance of BaseData_speed will have to compute the average of the data in its constructor even though this is the exact same number of all instances.
I'm thinking that this is as close as it gets though so I will accept this answer. Thanks again.

Sign in to comment.

More Answers (0)

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Products

Community Treasure Hunt

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

Start Hunting!