Developer Zone

Advanced Software Development with MATLAB

Properties of properties 7

Posted by Andy Campbell,

A while ago we discussed a good reason to appreciate and use MATLAB's model for properties. It had to do with encapsulation if you remember, and we discussed how MATLAB's properties are inherently encapsulated, preventing the need to write a bunch of boiler plate setters and getters.

There's more goodness where that came from! Another reason to use MATLAB's properties is to benefit directly from the fact that everything in MATLAB is considered an array that can be sliced and diced however you please.

The beauty if MATLAB is in its simplicity of handling arrays. Rather than requiring some external container like a List or a Vector of something, everything is inherently already a list and a vector! With this you can be free to vectorize to your heart's content, as well as index into your array using the powerful MATLAB syntax we all know and love.

This gets us back to object properties. If you use properties instead of setter and getter methods you benefit from all this indexing sugar for free.

Pulling on strings

Case in point: Let's say we love the new string class in MATLAB. After all what's not to love? The new MATLAB strings are rational and array oriented, opening us to fast and powerful manipulation techniques. A quick point is that we can indeed leverage this "arrayness" directly in the string class itself without writing our own object code. For example, if you wanted to quickly do some manipulation on a given string in order to get your priorities straight you can do so quite easily. Let's start by creating a string scalar:

openingLine = "It was the best of times it was the worst of times"
openingLine = 

    "It was the best of times it was the worst of times"

This is great, we can operate on this as a single string rather than the old character vector approach where each character is an element of the array. However, right now, I'd like to operate on this as an array of words, so let's split it up:

openingLine = split(openingLine)
openingLine = 

  12×1 string array

    "It"
    "was"
    "the"
    "best"
    "of"
    "times"
    "it"
    "was"
    "the"
    "worst"
    "of"
    "times"

Now that this is an array of words, we can easily have it conform to our nefarious demands! We can replace "times" with "blogs" simply using the power of indexing:

openingLine(contains(openingLine, "times")) = "blogs"
openingLine = 

  12×1 string array

    "It"
    "was"
    "the"
    "best"
    "of"
    "blogs"
    "it"
    "was"
    "the"
    "worst"
    "of"
    "blogs"

Note for MATLAB strings the replace function is actually easier than this, but I wanted to show the broader context of array indexing. In practice with strings we should use replace.

openingLine = "It was the best of times it was the worst of times"
openingLine = split(openingLine)
openingLine = replace(openingLine, "times", "blogs")
openingLine = 

    "It was the best of times it was the worst of times"


openingLine = 

  12×1 string array

    "It"
    "was"
    "the"
    "best"
    "of"
    "times"
    "it"
    "was"
    "the"
    "worst"
    "of"
    "times"


openingLine = 

  12×1 string array

    "It"
    "was"
    "the"
    "best"
    "of"
    "blogs"
    "it"
    "was"
    "the"
    "worst"
    "of"
    "blogs"

Make it your own

Alright, vectorization and indexing are powerful in the MATLAB environment, but how do we leverage this in our own objects? The answer is to use properties! Let's say we want to create our own fancy string class which adds to the capabilities of the string class we see in MATLAB. Perhaps we want to keep the underlying text content of the string pure an unchanged, but we want to add specific meta-data to individual elements. For example, in environments that support hyperlinks (like the MATLAB command window) I may want to add a hyperlink, but I don't want this hyperlink to modify the real text content. Maybe I'd like to print that to a text file and the hyperlink would get in the way. Let's look at how we can do this, and benefit from the indexing behavior of MATLAB's properties.

First, we should create our FancyString class. Note we leverage composition over inheritance (remember?) and we hold on to the "plain" string. However, the constructor passes all values through to the string constructor so that it feels like a normal string. Note we can also add converter methods for char, string, and cellstr so that we can combine these values together effortlessly.

classdef FancyString 
    
    properties
        PlainValue
    end

    
    methods
        function fancyString = FancyString(varargin)
            if nargin < 1
                % Supporting a zero argument constructor allows us to
                % preallocate. This is very powerful when creating arrays.
                % Note this constructor itself preallocates below.
                return 
            end
                        
            % Create the plain string
            plainValue = string(varargin{:}); 
            % Create the array of FancyStrings to match. 
            fancyString(numel(plainValue)) = FancyString; 

            % Each element of this FancyString array holds onto (composes)
            % a regular string scalar. Send each element of the string
            % array to each element of the FancyString array
            plainValueCell = num2cell(plainValue);
            [fancyString.PlainValue] = deal(plainValueCell{:});
        end
            
        function c = char(fancyString) % Allow conversion from char
            c = char(fancyString.PlainValue);
        end
        function c = cellstr(fancyString) % Allow conversion from cellstrs
            c = cellstr({fancyString.PlainValue});
        end
        function s = string(fancyString) % Allow conversion from strings
            s = [fancyString.PlainValue];
        end
    end
end

Does this work? Let's try it out with our string

fancyString = FancyString(openingLine);
[fancyString.PlainValue]'
ans = 

  12×1 string array

    "It"
    "was"
    "the"
    "best"
    "of"
    "blogs"
    "it"
    "was"
    "the"
    "worst"
    "of"
    "blogs"

Alright, so far so good. Now we can start adding our fanciness! What I'd like to do is add the ability to decorate each string element with a decorator, and introduce the notion of a decorated value of the string. It is here I'd like to add the hyperlinks. Then, when I want to send the output to the Command Window I send the decorated value, but when I want to send the output to a text file I print the plain text. First's let's add the notion of a default decorator, which does nothing when decorated:

classdef DefaultDecorator
    
    methods
        function decoratedString = decorate(~, undecoratedString)
            decoratedString =  undecoratedString;
        end
    end       

end

Now let's add a hyperlink decorator which decorates the string with a hyperlink tag. In this case we need the link target, but the link text should be taken as the value of the underlying string. I love how we can simply add these strings together to do this decoration!

classdef HyperlinkDecorator
   
    properties
        LinkTarget
    end
    
    methods
        function decorator = HyperlinkDecorator(linkTarget)
            decorator.LinkTarget = linkTarget;
        end
        function decoratedString = decorate(decorator, undecoratedString)
            decoratedString = "<a href=""" + decorator.LinkTarget + """>" + undecoratedString + "</a>";
        end
    end       
    
    
end

Alright, we are now set with our two decorators, let's use them with the FancyString:

classdef FancyString 
    
    properties
        PlainValue
        
        % Each element uses the "do nothing" decorator by default
        Decorator = DefaultDecorator; 
    end
    
    properties(Dependent)
        DecoratedValue
    end
    
    
    methods
        function fancyString = FancyString(varargin)
            if nargin < 1
                return
            end
                        
            plainValue = string(varargin{:});
            fancyString(numel(plainValue)) = FancyString;

            plainValueCell = num2cell(plainValue);
            [fancyString.PlainValue] = deal(plainValueCell{:});
        end

        function decorated = get.DecoratedValue(fancyString)
            % The decorated value returns whatever the decorator adds to
            % the plain string.
            decorated = fancyString.Decorator.decorate(fancyString.PlainValue);
        end
            
        function c = char(fancyString)
            c = char(fancyString.PlainValue);
        end
        function c = cellstr(fancyString)
            c = cellstr({fancyString.PlainValue});
        end
        function s = string(fancyString)
            s = [fancyString.PlainValue];
        end
    end
end

We've added a Decorator property as well as a dependent DecoratedValue property. Since MATLAB is built for arrays this property is inherently array oriented. Each element of the FancyString has it's own Decorator. Also, when we access the DecoratedValue property on the array, each element of the array get's it own call to the get.DecoratedValue function to return the correct property. Let's see this beauty in action.

First we can create the FancyString again, but this time add a decorator to make it interesting. Let's add a hyperlink to the blog everywhere we see the word "blogs" in the plain value.

fancyString = FancyString(openingLine);
[fancyString(contains([fancyString.PlainValue], "blogs")).Decorator] = deal(HyperlinkDecorator("http://blogs.mathworks.com/developer"));

Using MATLAB properties when writing the class, we've just leveraged indexing to add the desired hyperlink to those elements of the FancyString that contain the word blogs. Did it work?

decoratedValue = join([fancyString.DecoratedValue])
decorator = fancyString(end).Decorator
decoratedValue = 

    "It was the best of blogs it was the worst of blogs"


decorator = 

  HyperlinkDecorator with properties:

    LinkTarget: "http://blogs.mathworks.com/developer"

Note the real underlying string is still available, unchanged and ready to display as well

plainValue = join([fancyString.PlainValue])
plainValue = 

    "It was the best of blogs it was the worst of blogs"

Are you leveraging MATLAB properties effectively in your MATLAB objects? Are you leveraging the inherent benefits of their arrayness? Do tell!


Get the MATLAB code

Published with MATLAB® R2017a

7 CommentsOldest to Newest

David Barry replied on : 1 of 7

Another good post, thanks Andy. In my experience, users don’t immediately stumble across the deal function. I’ve seen code where people have resorted to a loop when they want to set the same property value in an object array. [objArray.Prop] = deal(‘PropValue’); is not immediately obvious. I think the thing people would first try would be objArray.Prop = ‘PropValue’; and then wonder what the “expected one output …” error was all about. Most then give up and just write a loop.

Any chance of a post or two on the new mocking framework?

Also, good point about deal. Indeed I see that my constructor uses deal when it doesn’t need to, because MATLAB doesn’t require deal anymore for cell arrays and structures. This certainly is easier, but your point is a good one, and I am still using deal in the line of code that assigns the decorator to a subset of the array.

Chris replied on : 4 of 7

Shouldn’t HyperlinkDecorator inherit from an DefaultDecorator Interface, so the calling class can sure, the decorate()-function is implemented?

Hey @Chris,

Great question, this is perhaps even worthy of a whole ‘nother post for itself! I think what you have in mind is definitely a good approach, but there are other philosophies and scenarios to explore. For a quick jaunt into the discussion (and if you haven’t heard the term before) google “duck typing” and see the conversation about this very question.

I’d like to address this in more depth in a future post, perhaps the next one.

Jan replied on : 6 of 7

Hi,

very interesting post, thank you.
My question is why to use composition instead of inheritance.
I’m trying to understand the composition over inheritance concept. I think it is clear to use composition if there is a “thing” which is composed by many other parts which are not of the same type or in the same logical domain.
But the FancyString is in the same logical domain and is even the same type as the String. Further it extends the String class’ functionality.

Can you please explain me why to use composition?

Thank you very much.
Jan

@Jan,

Thanks for the kudos. I am feeling overwhelmed at the prospect of answering your question in the form of a comment. There’s just not enough room and this could cover a whole series of blog posts in their own right.

At a very high level, favoring composition over inheritance is an established pattern because inheritance is a very strong form of coupling. The subclass is very tightly coupled to the base class and vice versa. This is especially true because even users of the code that operate on the base class can now operate on the subclass as well, and thus an entire code base can evolve which make both the base class and the subclass hard to change.

I think you have nailed a couple of the principles here, in that in this case it may indeed be a case where inheritance is ok, because we do want FancyString to be substitutable for string. In fact, that is why we created a string conversion method to allow anything that operates on a string to also be able to operate on a FancyString.

In this case, I used composition because string is Sealed. Why is string Sealed? I actually don’t know the answer to that, but it may range from string’s never allowing subclassing to maybe wanting to allow subclassing at some point but the extension model is not yet ready. As a principle, allowing subclassing can really restrict a base class’ ability to change and evolve, so in principle there should be a well formed model for extension. Maybe string is just not ready for this? I don’t know. As a class author I actually avoid subclassing anything that I don’t know has a well formed model for extension. In other words, did the base class author expect the class to be subclassed or did they just forget to Seal the class? If they didn’t expect it, they are more likely to make a change that breaks my subclass code in the future. If they did expect it, I look what how they recommend the extension to work, and what the contract is. So if I subclass it in a certain way, what do they require of me as I extend it, what benefits do they give me when I do, and what guarantees have they provided in their interface. Even if a class is not Sealed, if it doesn’t seem to have a well defined contract for extension, I am more comfortable using composition, which does have a contract (usage of the class through the public api for the user rather than an extension api).

Does that make sense? That’s like 4 or 5 different blogs posts munged together into one comment, let me know if it wasn’t all that clear.

Add A Comment

What is 1 + 6?

Preview: hide