Update a value class property only if required

32 vues (au cours des 30 derniers jours)
Christian Vetter
Christian Vetter le 13 Avr 2020
Commenté : Peng Li le 15 Avr 2020
Hello everyone,
I am new to object oriented programming and have some issues getting the behavior of my class in the way I want it. I have several input properties and a "dependent" output property. Because the algorithm to compute the output is numerically intensive, I do not want to handle it as an actual dependent property. It should only be updated if necessary. Also I want to be able to save my results to my hard drive, which would not work with a dependent property.
I got this basically working if my class is a handle class as shown in the example down below. But I do not like how handle classes function. In particular it can be very unintuitive to the user if they try something like this:
A = myclass(1,2);
B = A;
A.input1 = 5;
So here is my example with the handle class. Can anyone tell me if that can also be achieved with a value class? General improvement suggests are welcome as well :-)
Thanks a lot in advance!
classdef myclass < handle
properties
input1
input2
output
update_required
end
methods
function obj = myclass(value1, value2)
obj.input1 = value1;
obj.input2 = value2;
obj.output = [];
end
function set.input1(obj, value)
if obj.input1 == value
return
end
obj.input1 = value;
obj.update_required = true;
end
function set.input2(obj, value)
if obj.input2 == value
return
end
obj.input2 = value;
obj.update_required = true;
end
function value = get.output(obj)
if obj.update_required
disp('Update required.')
obj.output = obj.input1 + obj.input2;
obj.update_required = false;
else
disp('No update required.')
end
value = obj.output;
end
end
end
  3 commentaires
Peng Li
Peng Li le 13 Avr 2020
and you are setting another property within your set.input1, set.input2 methods. This is not recommended, and thus shouldn't be done. It's better to use an event listener to accomplish what you want, and thus you'll need to do a handle class.
And another comment is that, to make your set. function work for a value class, you need to return the obj within the set. method. You don't need an out variable for handle class as what you are working on all refers to that instance. But for value class, you are working on copies and if you don't return the obj, you loose it. Matlab will complain this as well.
for value class
obj = set.input1(obj, val)
for handle class
set.input1(obj, val)
Peng Li
Peng Li le 13 Avr 2020
within some modifications, you might be able to make it work with these all twisted set. methods, although it's not recommended.

Connectez-vous pour commenter.

Réponse acceptée

Peng Li
Peng Li le 14 Avr 2020
Modifié(e) : Peng Li le 14 Avr 2020
I think my solution works if you reset your update_required within your if...end block.
classdef myclass
properties
input1
input2
end
properties (Dependent = true)
output
update_required
end
properties (Hidden = true)
output_
update_required_
end
methods
function obj = myclass(value1, value2)
obj.input1 = value1;
obj.input2 = value2;
obj.output = value1 + value2;
end
function obj = set.input1(obj, value)
if obj.input1 == value
obj.update_required = false;
return
end
obj.input1 = value;
obj.update_required = true;
end
function obj = set.input2(obj, value)
if obj.input2 == value
obj.update_required = false;
return
end
obj.input2 = value;
obj.update_required = true;
end
function value = get.output(obj)
if obj.update_required
disp('Update required.')
obj.output = obj.input1 + obj.input2;
obj.update_required = false;
else
disp('No update required.')
end
value = obj.output_;
end
function obj = set.output(obj, val)
obj.output_ = val;
end
function val = get.update_required(obj)
val = obj.update_required_;
end
function obj = set.update_required(obj, val)
obj.update_required_ = val;
end
end
end
>> A = myclass(1, 2);
>> A
A =
Update required.
myclass with properties:
input1: 1
input2: 2
output: 3
update_required: 1
>> A.input1 = 1
A =
No update required.
myclass with properties:
input1: 1
input2: 2
output: 3
update_required: 0
>> A.input2 = 2
A =
No update required.
myclass with properties:
input1: 1
input2: 2
output: 3
update_required: 0
>> A.input1 = 5
A =
Update required.
myclass with properties:
input1: 5
input2: 2
output: 7
update_required: 1
>> A.input2 = 10
A =
Update required.
myclass with properties:
input1: 5
input2: 10
output: 15
update_required: 1
  4 commentaires
Matt J
Matt J le 15 Avr 2020
This creates Code Analyzer warnings. You could probably make it work if input1 and input2 were made to be Dependent properties as well. I don't think it's worth it, though...
Peng Li
Peng Li le 15 Avr 2020
yeah this is annoying and that's why i don't like this twisted way, although it seems to work. And i don't want to make input1 and input2 dependent, as conceptually I think they shouldn't be.

Connectez-vous pour commenter.

Plus de réponses (2)

Matt J
Matt J le 13 Avr 2020
Modifié(e) : Matt J le 13 Avr 2020
You have two separate issues that need to be addressed. The issue of handle class versus value class is the easier of the two. The only difference is that in a value class implementation, your set.prop() methods must return an output.
The second issue is whether input1 and input2 should be Dependent classes. They should, since it is dangerous for a non-Dependent property set or get method to reference other properties. This is true regardless of whether myclass is a value or handle class. Below is the value class implementation I would recommend.
classdef myclass
properties (Hidden)
Input1 %holds the data for "input1"
Input2 %holds the data for "input2"
output
end
properties
update_required;
end
properties (Dependent)
input1
input2
end
methods
function obj = myclass(value1, value2)
obj.Input1 = value1;
obj.Input2 = value2;
obj.output = [];
obj.update_required=1;
end
function input1=get.input1(obj)
input1=obj.Input1;
end
function input2=get.input2(obj)
input2=obj.Input2;
end
function obj=set.input1(obj, input1)
if obj.Input1 == input1
return
end
obj.Input1 = input1;
obj.update_required = true;
end
function obj=set.input2(obj, input2)
if obj.Input2 == input2
return
end
obj.Input2 = input2;
obj.update_required = true;
end
function value = get.output(obj)
if obj.update_required
disp('Update required.')
obj.output = obj.input1 + obj.input2;
obj.update_required = false;
else
disp('No update required.')
end
value = obj.output;
end
end
end
  4 commentaires
Matt J
Matt J le 14 Avr 2020
Modifié(e) : Matt J le 14 Avr 2020
As I think about it, I don't believe you can do this as a value class if the update of "output" has to be postponed until the moment it is needed. You can force an update whenever input1 or input2 are changed (see my 2nd answer), but the update has to occur at that moment.
Christian Vetter
Christian Vetter le 14 Avr 2020
@Peng Li,
No, this is wouldn't work. At least not in the way I indend. In the set-methods for the input variables, updated_required should be set to true. In the get-method for the output variable, the update should be executed if necessary and the updated_required should be set back to false. But this isn't going to work in a value class it seems. Because the get-method for the output variable cannot access/write the property obj.updated_required.

Connectez-vous pour commenter.


Matt J
Matt J le 14 Avr 2020
Modifié(e) : Matt J le 14 Avr 2020
Is there a reason you don't update when changes to input1 or input2 occur, like in the following?
classdef myclass
properties
input1;
input2;
output;
end
methods
function obj = myclass(value1, value2)
obj.input1 = value1;
obj.input2 = value2;
end
function obj=set.input1(obj, input1)
if obj.input1 == input1
return
end
obj.input1 = input1;
obj = update_output(obj);
end
function obj=set.input2(obj, input2)
if obj.input2 == input2
return
end
obj.input2 = input2;
obj = update_output(obj);
end
function obj=update_output(obj)
obj.output=obj.input1+obj.input2;
end
end
end
  2 commentaires
Christian Vetter
Christian Vetter le 14 Avr 2020
The problem is the calculation time. The actual problem will involve the creation and multiplication of big matrices. Each matrix entry will be calculated by solving an integral over Bessel functions. Hence, this might take several minutes. Assume I do something like that:
A = myclass(1,2);
% do something with A
A.input1 = 3;
A.input2 = 4;
% do something else with A
Not only would the set functions be called twice within the constructor method (this could for sure be avoided more or less easily) but it would also be called everytime I change a single input. This means I have to wait several minutes after changing each input variable instead of just waiting once at the end when I actually need the output.
So I guess I have to live with the fact that my class needs to be a handle class. In this case I can use PostSet listeners with AbortSet on my inputs to toggle the update_required value and a PreGet listener on output property.
Still, thanks a lot!
Matt J
Matt J le 14 Avr 2020
Modifié(e) : Matt J le 14 Avr 2020
So I guess I have to live with the fact that my class needs to be a handle class.
No, that's not the only solution. Instead of a get.output() method, you could instead use an ordinary method like below. This does not enable the syntax you were looking for, but it does allow you to use value classes and still get the computational efficiency you're after. I would also argue that it is probably better to undertake a large computation in an explicit function call like this, rather than have it hidden from view in a property access operation.
function obj=update_Object(obj)
if obj.update_required
disp('Update required.')
obj.output = obj.input1 + obj.input2;
obj.update_required = false;
else
disp('No update required.')
end
end

Connectez-vous pour commenter.

Catégories

En savoir plus sur Software Development Tools dans Help Center et File Exchange

Produits


Version

R2019b

Community Treasure Hunt

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

Start Hunting!

Translated by