Using narrower property validation definitions in subclasses + using an abstract class for property validation

6 ビュー (過去 30 日間)
Hello,
BLUF: It is not clear to me why I can overload methods, but not property validation/class assertions, in subclasses.
I'll lead off by saying that I don't believe what I want to do right now is possible in Matlab 2021a. I do want to know:
  1. Is there a best practices workaround
  2. Is there a good reason it isn't supported
  3. Is this something we should get into the feature request pipeline
Let's start with a refrigerator analogy; I initially want to create an abstract superclass called container, that has a bunch of methods for e.g. adding contents, removing contents, and checking whether the container is full.
classdef (Abstract) container < handle
properties (Abstract)
contents
capacity
end
methods
tf = addContent(this,content) % e.g. add a brownie to the top of the stack, return false if the container is full
content = rmContent(this) % e.g. take a brownie off the top of the stack
tf = isFull % return true if numel(contents) == capacity
end
end
Now, let's say I want to make a specific container that can hold eggs. Seems simple enough, and I can even overload the rmContent so that I can grab a specific egg, rather than the top of the stack. I am also going to make an abstract class called "eggShapedItems", since my container could hold chicken eggs, cadbury eggs, easter eggs, etc. I will also make a concrete egg class
classdef eggCarton < container
properties
contents eggShapedItem
capacity = 12
end
methods
content = rmContent(this,index)
end
end
classdef (Abstract) eggShapedItem < matlab.mixin.heterogeneous
end
classdef chickenEgg < eggShapedItem
end
I have gone with heterogeneous so that my container can hold a mixed array of eggShapedItems.
But oh no! I immediately have a problem instantiating an eggCarton: "Using size and validation functions on an inherited property is not supported. Property 'contents' is defined as abstract property in superclass 'container".
Hmm, what if I change contents to concrete in the superclass:
classdef (Abstract) container < handle
properties
contents
end
end
New error: "Cannot define property 'contents' in class 'eggCarton' because the property has already been defined in the superclass 'container'"
In other words, while I can overload methods in the subclass, I cannot overload properties (or their validation).
Ok...so I can get around this by not defining the property contents at all in the superclass 'container' I am not happy about this for two reasons***: ***(there is another issue, see at bottom)
  1. The concrete methods of 'container' still assume that there is in fact a property called 'contents', even if it isn't defined in the superclass. I want these methods to be inherited by all containers, and redefining 'contents' in every subclass is bad practice.
  2. I am blocked from further narrowing the definitions of either 'eggShapedItems' or 'eggCarton'. e.g.:
classdef smallEggCarton < eggCarton
properties
contents smallEggShapedItem
end
end
classdef smallEggShapedItem < eggShapedItem
end
While this may be a goofy example, I think it is clear that there would be utility in being able to progressively overload the property validations through a subclass tree. In this case, I have made each class of 'contents' a subclass of the one used in the superclass; however, there would also be utility in using easily converted classes, e.g.:
classdef eggCarton < container
properties
capacity uint16
end
end
classdef smallEggCarton < eggCarton
properties
capacity uint8
end
end
If we could overload property definitions, onus is then on me to make sure that inherited methods are compatible, or that I overload them in the subclass to be compatible. I can live with that. AFAIK, that is not possible right now; the property definition is locked in by the superclass - only the default value can change, and that only if the property is abstract in the superclass.
Finally, I noted that there is another issue when trying to intantiate an eggCarton: "Error defining property 'contents' of class 'eggCarton'. Class eggShapedItem is abstract. Specify a default value for property contents."
This is a seperate, but also frustrating restriction on the property validation in Matlab; in this example, the contents of my eggCarton must be instances subclassed to 'eggShapedItem', but I would never make a concrete 'eggShapedItem', it should be an Abstract class.
As before, there are workarounds; make eggShapedItem concrete, or remove the class from the property definition entirlley, and move the validation into an overloaded set.contents(this,content) method. Again, there are drawbacks:
  • I lose all of the code completion help that comes with defining the property's class in the prop block.
  • This also strikes me as sloppy coding
  • I could understand the issue if I tried to create an array on instantiation (e.g. contents (12,:) eggShapedItem). and I do get that Matlab is trying to put in an empty (e.g. this.contents = eggShapedItem.empty), which returns an error for abstract classes.
Thanks,
Dan
  2 件のコメント
Matt J
Matt J 2021 年 4 月 2 日
編集済み: Matt J 2021 年 4 月 2 日
I could understand the issue if I tried to create an array on instantiation (e.g. contents (12,:) eggShapedItem). and I do get that Matlab is trying to put in an empty (e.g. this.contents = eggShapedItem.empty), which returns an error for abstract classes.
That doesn't sound like a drawback. It sounds like you understand why it's not allowed! In any case, a concrete property needs to be able to hold a value at all times, so you have to either specify a default eggShapedItem object or reframe the property validation so as to make an [] value legitimate.
Daniel Plotnick
Daniel Plotnick 2021 年 4 月 5 日
Thanks. I think one way to make sure you cannot actually instantiate an eggShapedItem is to protect the constructor, rather than make it abstract:
classdef eggShapedItem
methods (Access = 'protected')
function this = eggShapedItem()
end
end
end
This ensures that subclasses that are meant to be concrete can still properly be instantiated, while preventing the instantiation of a pure superclass object.

サインインしてコメントする。

採用された回答

Matt J
Matt J 2021 年 4 月 2 日
編集済み: Matt J 2021 年 4 月 6 日
There is a lot to unpack in your question, but basically the solution I would recommend is that you make the abstract property a Dependent property in the subclass, like the example below. In the subclass, 'contents' is just an alias for a hidden concrete property 'contents_' which can be given any validation scheme you wish (EDIT: in addtion to those already imposed for 'contents' in the superclass).
As for the why's - basically the design philosophy followed by MATLAB OOP is that what is valid/invalid for a concrete property in a superclass must remain valid/invalid in any subclass. Therefore, you are prevented from overloading validation, and similarly set.property() and get.property() methods, as a way of ensuring that a subclass object remains a valid example of a superclass object. It makes a certain amount of sense because non-overloaded superclass methods often rely on input objects satisfying superclass property restrictions. If you could overload property validation to violate those restrictions, superclass methods may no longer work for child class objects.
In any case, the idea that you should be able to overload these things has been raised before and rejected by MathWorks, so I doubt it would be worthwhile to propose it to them again.
classdef myclass
properties (Abstract)
contents uint32 {mustBeNonnegative}
end
end
classdef mysubclass<myclass
properties (Dependent)
contents
end
properties (Hidden)
contents_ {mustBeGreaterThan(contents_,10)} = 12
end
methods
function val=get.contents(obj)
val=obj.contents_;
end
function obj=set.contents(obj,val)
obj.contents_=val;
end
end
end
  5 件のコメント
Matt J
Matt J 2021 年 4 月 16 日
Yeah. Well, there are backdoor methods to accomplish this, like in the following:
classdef (Abstract) container < handle
properties (Abstract)
contents
end
methods (Static, Abstract)
tf=contentsValid(val);
end
methods
function obj=set.contents(obj,val)
assert( obj.contentsValid(val), 'Invalid contents');
obj.contents=val;
end
end
end
classdef eggCarton < container
properties
contents
end
methods (Static)
function tf=contentsValid(val)
tf=isa(val,'eggShapedItem');
end
end
end
classdef smallEggCarton < container
methods (Static)
function tf=contentsValid(val)
tf=isa(val,'chickenEgg');
end
end
end
Austin Spencer
Austin Spencer 2021 年 4 月 20 日
Yeah. Well, there are backdoor methods to accomplish this, like in the following:
I totally agree, there are ways to enforce data types manually, like you show. I think these are valid approaches for achieving certain goals and they have existed since long before property validation what a thing.
What I would love to see though (and this comment is more aimed at Mathworks) is type validation via definition (or what you might call "at compile time") rather than "check at run time". In my view, one of the main benefits of property validation is to provide a clear definition of acceptable data type for those using, modifying, or subclassing the class. It's basically a form of "structural documentation" that the person reading the code (or MATLAB's linter) can quickly see and interpret. Otherwise, there's not much marginal benefit of property validation other than making type checking slightly more succinct. Maybe people will think that this borders too closely on expecting MATLAB to have strong typing, but I think this is more in the realm of having a complete and effective OOP system.

サインインしてコメントする。

その他の回答 (0 件)

カテゴリ

Help Center および File ExchangeConstruct and Work with Object Arrays についてさらに検索

製品


リリース

R2021a

Community Treasure Hunt

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

Start Hunting!

Translated by