OOP - convert base class object to sub class object

22 views (last 30 days)
Is it possible that a base class object "updates" itself into a sub class object by its own method? E.g., I have a rack that can hold up to 4 modules (objects) of different types (via handle) but the existing modules are unknown. Now, each module has (amongst other things) the functionality to check its own type by a method; to not repeat code there is a base class all specific modules inherit from. It would be nice, though, if I first can create a base class module object that checks its type and then expands functionality depending on its return.
Kind of the handle changes from module.base_modul to module.specific_modul with a methode that only exists in the base_modul class and later in its sub class specific_modul. I hope my thoughts are understandable?
  6 Comments
Guillaume
Guillaume on 11 Jun 2019
The hierarchy looks fine to me. I possibly would have another derived class from WG5K_slot called Unknown_slot and fill the slots array with that in the WG5K_rack class when it's constructed.
The only thing that looks odd is the duplicate module identification code in the slot and rack class. I probably would have just it as a static method of the base slot class as I initially outline.
Konrad Warner
Konrad Warner on 12 Jun 2019
Edited: Konrad Warner on 17 Jun 2019
All right, thanks.. but if I fill the slots array with Unknown_slot objects initially, how can I replace them if the right module is found? Do I need to delete the unknown one or can I just overwrite the object handle?
And I also thought to have a static checkModule or slot_number_to_setRTS/DTR methode but in both cases I access the SerialPort object and that's not allowed because it's not static?
I might try to summarize why I feel going round circles: First I need the SerialPort object of course. Then I would like to go through the different slots (RTS/DTR pins) and send/receive the check commands. But this procedure is actually functionality of the module (derived from slot) class which are not instantiated yet. So, to avaoid duplicaed code: I could use a static check method (not possible I think?) or I initially create a unknown_slot object and replace the object later on by the right module object (where I'm back on my original question in some way: can the object "replace" itsef?)
...hopefully somewhat comprehensible why I have difficulty finding a clean solution, I'm really not very experienced in OOP.

Sign in to comment.

Accepted Answer

Steven Lord
Steven Lord on 23 May 2019
I think this approach has a couple problems, two conceptual and one specific to Guillaume's implementation.
Conceptually, with this approach each time you add a new subclass you must modify the base class method IdentifyModule to add a new case. That is a violation of the Open-closed principle of OO design. Part of Meyer's quote on that Wikipedia page is "When a descendant class is defined, there is no need to change the original or to disturb its clients."
Also conceptually a base class object may not be convertible into a subclass object. All subclass objects should be substitutable for a base class object (the Liskov substitution principle) but the reverse is not necessarily true. If I have a hierarchy where Circle (with property Radius) and Triangle (which does not have a property Radius) are subclasses of Shape (with property Area) it makes sense to ask what a Circle object's Area is (which is a question you can ask any Shape) but it may not make sense to ask a general Shape (which may be a Triangle) what its Radius is.
As Guillaume has implemented it, IdentifyModule can only be invoked if slot isa ModuleBase (which includes subclasses of ModuleBase.) If IdentifyModule were a plain old factory function (not a method) or were a Static method of ModuleBase, it could be invoked either using the name of the class (ModuleBase, ModuleType1, or ModuleType2) and an arbitrary input or could be written to be invoked with an instance of the class.
Depending on what you're trying to do with this class hierarchy, Konrad, I think what you may want is to build a heterogeneous class hierarchy. You could have a moduleBase class that subclasses matlab.mixin.Heterogeneous and four individual module subclasses that subclass moduleBase. If you did that and made an array containing instances of multiple different modules the array would be listed in the workspace as a moduleBase array but the individual elements would remain instances of their specific subclass. You could run any method that's defined Sealed on the base class on the array as a whole, or you can run methods defined for a specific subclass on the elements of that type in the array. You can get properties defined on the common superclass of the array.
So using my earlier example an array containing both Circle and Triangle objects that subclass from a matlab.mixin.Heterogenous Shape class would show up in the workspace as a Shape array. You could ask for the Area of the array and get the Area of each object, but asking for the Radius would error.
  1 Comment
Guillaume
Guillaume on 24 May 2019
Edited: Guillaume on 24 May 2019
Oops! The factory method was always meant to be static (I need more sleep...). It could also be a completely external function, but I personally prefer the factory method to be part of a class (I guess that's my C# background showing).
At no point, was I intending that an instance of ModuleBase would be created (the class is abstract!) and then converted to a derived object.
I would indeed also derive from matlab.mixin.Heterogeneous but that's not what is going to help with creating objects of the right class.

Sign in to comment.

More Answers (1)

Guillaume
Guillaume on 23 May 2019
Edited: Guillaume on 24 May 2019
Th design you describe is fairly common and sounds sensible but I'm not entirely clear on the finer point, particularly on how the module type is stored/retrieved
Typically, with a design like that the base class is abstract and the module type is simply determined by the type of the derived class. So, you can never construct a module without a type. What you can have though is a factory method in the base class that construct objects of the correct class:
classdef (Abstract) ModuleBase < handle %the fact that the class is a handle class is irrelevant here
properties %properties common to all modules
slot;
end
methods
%base class constructor
function this = ModuleBase(slot)
this.slot = slot;
end
end
methods (Static)
%factory method
function module = IdentifyModule(slot)
%Identify the module in the given slot and return an instance of the module of the correct type
moduleid = queryslot(slot); %get identification of module from slot. No idea how you do that
%using switch here. In practice I'd probably build the class name from the module id and use feval to call the appropriate constructor.
switch(moduleid)
case 'type1':
module = ModuleType1(slot); %call constructor of Type1 module
case 'type2':
module = ModuleType2(slot); %call constructor of Type2 module
end
end
end
end
classdef ModuleType1 < ModuleBase
properties
%properties specific to Type1
end
methods
function this = ModuleType1(slot)
this@ModuleBase(slot);
%set other properties
end
end
end
classdef ModuleType2 < ModuleBase
properties
%properties specific to Type2
end
methods
function this = ModuleType2(slot)
this@ModuleBase(slot);
%set other properties
end
end
end
edit: As pointed out by Steven, the factory method should have been static

Products


Release

R2019a

Community Treasure Hunt

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

Start Hunting!