2

Lets say I have a superclass called rectangle:

classdef Rectangle
    properties
        width
        height
        x0 = 0
        y0 = 0
        angle = 0
    end
    methods
        function obj = Rectangle(x0, y0, width, height, angle)
            obj.x0 = x0;
            obj.y0 = y0;
            obj.width = width;
            obj.height = height;
            obj.angle = angle;
        end
    end
end

And I have a subclass called Map, in which I want all properties to be read-only once set:

classdef Map < Rectangle
    properties (SetAccess=private)
        filename
    end
    methods
        function obj = Map(filename)
            % Get info from map using a function that uses geotiffread
            [x0, y0, width, height] = GetInfoFromMap(filename);
            obj = obj@Rectangle(x0, y0, width, height, 0);
            obj.filename = filename;
        end
    end
end

How can I make the inherited properties of Rectangle read-only within Map? I want the properties of any stand-alone (non Map) Rectangle object to remain changeable.

And also, how can I ensure that some of those properties can take only certain values? i.e. for my purposes a rectangle can have any angle, but I'd expect a map to always have an angle of 0. But if I try to create a set.angle method for Map to ensure that angle can only be 0, I get an error telling me that "Cannot specify a set function for property 'angle' in class 'BackgroundMap', because that property is not defined by that class."

EddyTheB
  • 3,100
  • 4
  • 23
  • 32
  • 1
    How would you do something like that in other oop languages? Would you override the superclass properties using more restrictive qualifiers (e.g. `private` instead of `public`/`protected`)? Regarding MATLAB - a common (albeit not very oop; which I don't like) approach would be to break the inheritance relation, and make `Map` a class in its own which has a property that points to a certain `Rectangle` and extend `Rectangle` from `handle`.. One more question - suppose you inherited the properties in `Map`, will they be fixed, or perhaps changeable should casting to the superclass be performed? – Dev-iL May 07 '15 at 11:20
  • I don't know, MATLAB is my first exposure to OOP, aside from a very brief introduction in python which never ventured as far as inheritance. I'm aware that MATLAB is not an ideal OOP language but it's what we're required to use at work. To be honest I had just assumed that you would be able to set constraints on inherited properties, but perhaps this is not the case. Thanks for the hint regarding breaking the inheritance. – EddyTheB May 07 '15 at 13:21

1 Answers1

2

This seems like a case where you should prefer composition over inheritance.

This idea is mentioned in one of the comments (@Dev-iL), where it's described as "not very OOP". I disagree - although inheritance is the right design in many cases, composition is often preferable. (See here and here for some discussion of that). One of the typical reasons why one might prefer composition is that inheritance exposes the subclass to the details of the superclass, in other words it breaks encapsulation - and that's what's happening in your implementation of Rectangle and Map.

I would suggest that instead of Map inheriting from Rectangle, it should have a Rectangle as a private (maybe hidden) property. So you should think of a map not as being a type of rectangle, but as having an associated rectangle that it delegates some of its functionality to.

Once you've done that, you can then give Map some properties width, height etc, and make them GetAccess=public, SetAccess=private, Dependent. Then within Map, add a get method for each, which just gets the property of the underlying Rectangle and passes it through. Note that Map doesn't need an angle property, as it's always zero.

classdef Map < handle
    properties (SetAccess=private)
        filename
    end
    properties (Hidden, GetAccess = private, SetAccess-private)
        rectangleDelegate
    end
    properties (Dependent, GetAccess = public, SetAccess = private)
        width
        height
        x0
        y0
    end
    methods
        function obj = Map(filename)
            % Get info from map using a function that uses geotiffread
            [x0, y0, width, height] = GetInfoFromMap(filename);
            obj.rectangleDelegate = Rectangle(x0, y0, width, height, 0);
            obj.filename = filename;
        end
    end
    methods
        function val = get.width(obj)
            val = obj.rectangleDelegate.width;
        end
        % other gets
    end
end
Community
  • 1
  • 1
Sam Roberts
  • 23,951
  • 1
  • 40
  • 64
  • Thanks. That solution works and you explanation has improved my understanding of OOP. The only change I've made is to make the GetAccess of rectangleDelegate public, because I want access to some of the methods contained within Rectangle (I left the methods out of my original example for brevity). For example, if I want the area of the map then I can do Map.rectangleDelegate.area, where area is a method of the Rectangle class. I suppose I could also write small one line functions for Map which call the same functions in Rectangle. – EddyTheB May 08 '15 at 09:04
  • 1
    @EddyTheB Of course it's your choice, but I would recommend that you don't do that. The whole point of the Delegate pattern is that you don't expose the Delegate publicly, but instead _internally_ delegate functionality to it. The user of the outer class shouldn't know it's there. I would recommend adding the extra properties to `Map`, and then writing the one line `get` functions that call Rectangle and pass the results through. – Sam Roberts May 08 '15 at 12:09
  • Fair enough, I'll do it without. Thanks. – EddyTheB May 08 '15 at 12:48