Nested structs and objects - do or don't?
6 views (last 30 days)
Show older comments
I have a question on nested objects. Take for example an object array of class 'house' which have a fixed number of floors whose properties are defined by a class 'story' (to avoid use of builtin 'floor'):
classdef house
properties
number uint32
location string
story_1 story
story_2 story
% story_3 story etc...
end
methods % Construct array of n houses
function obj = house(n)
obj.story_1 = story;
obj.store_2 = story;
if nargin ~= 0
obj(n,1) = obj;
end
end
end
end
where
classdef story
properties
size_in_square_ft double
number_of_rooms uint8
% etc...
end
end
This would be an intuitive way of structuring the data since each 'house' object in the array holds the data for one house. On the other hand, I'm struggling to assign values for floors of multiple houses simultaneously. While this works:
Houses = house(3);
numbers = num2cell(1:3);
[Houses(:).number] = numbers{:};
This does not (since Matlab does not allow simultaneous assignment to subfields):
Houses = house(3);
numbers_of_rooms_story_1 = num2cell([2 1 3]);
[Houses(:).story_1.number_of_rooms] = numbers_of_rooms_story_1{:};
I know I could do the assignments in a loop, but that would likely be slower than vectorized assignment (which may become an issue when dealing with large object arrays and multiple properties). Hence my question: Are "nested objects" bad practice (if so, what should I use instead)? (And more generally, is it bad practice to use an OOP approach for processing large amounts of data?)
4 Comments
Accepted Answer
Matt J
on 15 Apr 2021
Edited: Matt J
on 15 Apr 2021
Ultimately, it depends on the situation and the data access patterns that you are going to need. As a rule of thumb, I don't think there is anything wrong with nested objects. However, I do try for OOP designs where you have scalar parent objects with array-valued property objects. That tends to make the property access options the neatest and most efficient.
Whether this rule will always be applicable I cannot say, but I think it does apply well to the house application and will work more smoothly than what you've attempted. In particular, it doesn't make much sense to me that you would want to assign number_of_rooms values to the same floor across multiple houses, like in your example. What if different houses have different numbers of floors? I think it more likely that you would want to assign number_of_rooms values across multiple stories in the same house, and so I would design the classes as follows:
classdef story
properties
size_in_square_ft double
number_of_rooms uint8
% etc...
end
end
classdef house
properties
number uint32
location string
stories story
end
methods
function obj=house(numFloors)
obj.stories(numFloors)=story;
end
end
end
This allows you to do things like,
House=house(3);
[House.stories(1:3).number_of_rooms]=deal(4,4,3)
If you need to then consider arrays of house objects, I think it would be natural to process them using for-loops. because I would expect the contents of houses to be rather inhomogeneous.
2 Comments
More Answers (1)
Steven Lord
on 15 Apr 2021
Off the top of my head I'd probably use a three tier approach, something along the lines of the following untested code:
classdef room
properties
squareFootage double;
description % kitchen, bedroom, bathroom, etc.
end
end
Then each story object would contain an array of room objects. If we want to know the square footage of a story, modulo having very thick walls that aren't counted as part of any room, it would be the sum of the square footage of the rooms.
classdef story
properties
theRooms (1, :) room;
end
properties(Dependent)
squareFootage
end
methods
function sf = get.squareFootage(obj)
sf = 0;
for whichRoom = 1:numel(obj.theRooms)
sf = sf + obj.theRooms(whichRoom).squareFootage;
end
end
end
end
And finally a house would have an array of stories. A house's square footage is the sum of the square footage of its stories.
classdef house
properties
stories (1, :) story;
end
properties(Dependent)
squareFootage
end
methods
function sf = get.squareFootage(obj)
sf = 0;
for whichStory = 1:numel(obj.stories)
sf = sf + obj.stories(whichStory).squareFootage;
end
end
end
end
There's some duplication between house's and story's get.squareFootage methods. I might abstract that out into an aggregateSquareFootage function:
function sf = aggregateSquareFootage(listOfAreas)
sf = 0;
for whichArea = 1:numel(listOfAreas)
sf = sf + listOfAreas(whichArea).squareFootage;
end
end
and turn the methods into:
function sf = get.squareFootage(obj)
sf = aggregateSquareFootage(obj.stories);
end
% or
function sf = get.squareFootage(obj)
sf = aggregateSquareFootage(obj.theRooms);
end
Regarding this comment from the original post:
On the other hand, I'm struggling to assign values for floors of multiple house simultaneously.
Perhaps that's a sign that the number of rooms on floor 1 of a house may not be really closely related to the number of rooms on floor 2 of that house or on floor 1 of the house next door. In reality there may be a relationship especially if all the houses were build to the same plan, but in that case I'd probably construct a plannedHouse and make copies of it to represent the buildings in the housing development / condo association / neighborhood / etc.
plannedHouse = house(etc);
houses = repmat(plannedHouse, 1, numHouses);
1 Comment
See Also
Categories
Find more on Construct and Work with Object Arrays in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!