2

I'm trying to figure out how to ask the user whether they want to replace the previous object of the same class with the default object, or simply use the previous object, when calling the constructor.

I'm looking for actions in both these cases:

>>obj = Obj()
'obj' already exists. Replace it with default? (y/n): y
%clear obj and call default constructor to create new obj

>>obj = Obj()
'obj' already exists. Replace it with default? (y/n): n
%cancel call of Obj()

How would I do this? I've messed around with the default constructor, to no avail.

EDIT: If it makes any difference, Obj is a subclass of Handle.

Nick Sweet
  • 2,030
  • 3
  • 31
  • 48
  • if you want to make sure only one instance exists, you could implement the [singleton pattern](http://en.wikipedia.org/wiki/Singleton_pattern) – Amro Jul 11 '13 at 17:28
  • do you mean you want to check that the variable you are assigning into `obj` doesnt already exist? – Amro Jul 11 '13 at 18:35
  • I want to provide three options when calling Obj(): if Obj doesn't exist, Generate obj automatically with no prompt; if Obj already exists, ask the user whether he/she wants to use the current version or replace it with the default. – Nick Sweet Jul 11 '13 at 18:43
  • `Obj` is really a class. lowercase `obj` in your example above is the variable that holds an instance of that class (i.e. an object). Are you trying to prevent overwriting `obj` variable, or you want to make sure there is only one object of type `Obj` in the entire workspace? – Bee Jul 11 '13 at 18:47
  • Sorry, I mistyped. I'll try again, with proper capitalization: if obj doesn't exist, Generate obj automatically with no prompt; if obj already exists, ask the user whether he/she wants to use the current version or replace it with the default. – Nick Sweet Jul 11 '13 at 18:50
  • And yes, it's preventing the overwriting of `obj`. – Nick Sweet Jul 11 '13 at 18:50
  • As far as I know there is no way to prevent a Matlab assignment from within the class. You cannot prevent `obj` from being overwritten by changing anything inside `Obj` class. – Bee Jul 11 '13 at 18:52
  • It's because constructors by design have to return a proper instance of the object (it didn't used to be like this in previous versions of Matlab). Anything returned by the constructors will overwrite the current value of `obj`. Use an auxiliary function to create `obj` as others have mentioned in their responses. – Bee Jul 11 '13 at 18:53
  • 2
    Im sorry but that's just bad coding, I suggest you rethink your approach. Why do you want to do such a thing anyway? The user should be free to chose whatever variable they like to store the class instance.. – Amro Jul 11 '13 at 21:46
  • If felt like a challenge for me at first, and as I have briefly shown it is possible to accomplish with various hacks, but I finally agree with Amro. I think the intention is manifested the moment I press enter in the command window and overwrite the object, thus what is the need for a double confirmation? If the call is wrapped inside a function, then delegate checks to the function not to the class constructor. – Oleg Jul 12 '13 at 00:13
  • The thing is, I'm calling this from a script, and I want to make sure that someone doesn't accidentally overwrite an object that's meant to persist between scripts. They're never explicitly creating the object, but, since that object is needed in the script and might not exist, I figured another layer of protection would be required. – Nick Sweet Jul 12 '13 at 01:57
  • You're trying to implement something akin to a `const` instance of a class, with the exception of still wanting the option to overwrite it. In many other languages, you'd overload the assignment operator for this purpose. As this is [not possible in MATLAB](http://stackoverflow.com/questions/8219116/matlab-overload-assignment-operator), you'll have to resort to dirty hacks, like the one @OlegKomarov showed you. If you just want the equivalent of `const`, use a wrapper with [this](http://stackoverflow.com/questions/1773850/constants-in-matlab) technique. – Rody Oldenhuis Jul 12 '13 at 07:58
  • Although I agree with @Amro and Oleg that this sort of thing should *never* appear in any production code, I can still see that it can be useful in classrooms, for means of self-protection with expensive-to-instantiate-classes when you are at the fool-around stage, etc. Therefore, I'd suggest to Oleg to undelete his answer, and modify it with things like `onCleanup` and overloading `delete` (to prevent it from being `clear`ed as well), and an **explicit and elaborate explanation why it is a bad idea for production code**; Oleg deserves this bounty! – Rody Oldenhuis Jul 12 '13 at 08:30
  • If I were to rank MATLAB programming approaches in descending order of automation, the list would be: class, function, script and command line execution. With this in mind, my approach with scripts is to load into workspace at the beginning and save at the end. If you need to ensure continuity between scripts I would probably refactor into a function. – Oleg Jul 12 '13 at 08:31
  • @OlegKomarov: More than a few times I've had to deal with classes that took just long enough to re-initialize to be annoying, and just too large to store and retrieve from disk without annoyance. When doing trash calculations, the proposed mechanism would help. Once again, this is all too easily degraded into a discussion much like the one on `goto`; it's evil how most people use it, therefore, considered useless. I disagree; there are a few fringe cases where `goto` actually saves a lot of work. It's dangerous, but not *always* useless (I'll still advise people to *never* use it though :) – Rody Oldenhuis Jul 12 '13 at 08:41
  • As someone without multiple years of experience with matlab, this is a really helpful discussion, and, even though I didn't get the answer I wanted (something as simple and elegant as the `const` solution, I think I got a better answer. Thanks! – Nick Sweet Jul 12 '13 at 16:28
  • And yes, @OlegKomarov – please re-add your answer so I can give you some sweet, sweet reputation! – Nick Sweet Jul 12 '13 at 16:29
  • @RodyOldenhuis I Undeleted the question ad modified the overwriting behaviour to be less hackish. I'd appreaciate your input to improve the answer (or we can make it a multiple author one, although not sure what's the procedure). – Oleg Jul 15 '13 at 08:15

3 Answers3

4

The following solution stems from several workarounds/hacks and is not part of the standard MATLAB's OO constructs. Use with caution.

You need to:

  1. evalin() into the 'caller' workspace the names and classes of the 'base' workpsace variables
  2. retrieve the last executed command
  3. extract the name of the assigned variable with e.g. regexp()
  4. compare names and classes. If a total match occurs, i.e. the variable in the 'base' workspace is being overwritten with a new instance of the same class, ask the user for input(). If the user chooses to preserve the existing object, overwrite the new instance with the existing one through evalin('caller',...).

The class foo:

classdef foo < handle
    properties
        check = true;
    end
    methods
        function obj = foo()
            % variable names and sizes from base workspace
            ws = evalin('base','whos');

            % Last executed command from window
            fid = fopen([prefdir,'\history.m'],'rt');
            while ~feof(fid)
                lastline = fgetl(fid);
            end
            fclose(fid);

            % Compare names and classes
            outname = regexp(lastline,'\<[a-zA-Z]\w*(?=.*?=)','match','once');
            if isempty(outname);  outname = 'ans'; end

            % Check if variables in the workspace have same name
            idx = strcmp({ws.name}, outname);
            % Ask questions
            if any(idx) && strcmp(ws(idx).class, 'foo')
                s = input(sprintf(['''%s'' already exists. '...
                     'Replace it with default? (y/n): '],outname),'s');
                % Overwrite new instance with existing one to preserve it
                if strcmpi(s,'n')
                    obj = evalin('caller',outname);
                end
            end
        end
    end
end

Class in action:

% create class and change a property from default (true) to false
clear b

b = foo
b = 
  foo with properties:
    check: 1

b.check = false
b = 
  foo with properties:
    check: 0

% Avoid overwriting
b = foo
'b' already exists. Replace it with default? (y/n): n

b
b = 
  foo with properties:
    check: 0

The weaknesses (see points above):

  1. applies only to cmw line and script executed commands, not functions (see link to extend to function calls). Also, might break in case of problems reading history.m.
  2. the current regex fails on a==b.
  3. Dangerous because the evalin() on user input leaves potential security threats open. Even if the input is filtered with the regexp and the string comparison, the construct might pose a problem if the code is revisited later on.
Community
  • 1
  • 1
Oleg
  • 10,406
  • 3
  • 29
  • 57
  • It's the most convoluted solution ever (in a good way!) You still cannot prevent `obj` from being overwritten if the user chooses `no`. The constructor will return an instance of the object no matter what. Also a persistent variable inside a static method would do the same, but it still fails to stop the assignment. – Bee Jul 11 '13 at 18:57
  • +1: you deserve it; good one on the re-assignment to the base variable name instead of the `error` mechanism. I tried overloading the `delete` method in order to prevent `clear` from deleting class instances without user confirmation, but apparently, MATLAB invalidates the link between the variable name in the workspace and the actual class instance *before* calling the `delete` method. This makes it *impossible* to use the `delete` method to prevent a `clear`, unless you overload the built-in `clear` command itself (***NOT ADVISED***). So: this is the final form :) – Rody Oldenhuis Jul 15 '13 at 09:50
  • +1: I agree Oleg deserves the bounty, even though it's too hackish for me :) – Amro Jul 15 '13 at 21:51
2

Singleton

try this, not sure if you are familiar with it, but this mean, you only have one global instance of this specific object.

aah134
  • 860
  • 12
  • 25
  • There are times when I'll want to have two of the same objects exist, though, so a singleton pattern won't work. – Nick Sweet Jul 11 '13 at 18:00
  • This comment is not compatible with your other comments. How do you expect to have two instances of `Obj` class inside one variable `obj`? Again the question comes down to whether you want to prevent `obj` variable to be overwritten or prevent another instance of `Obj` class to be created inside the current workspace regardless of the variable that is currently being assigned to. – Bee Jul 11 '13 at 19:06
  • you can try to modify it into a singleton pattern that contain an id, in which new id get a new object, and same id, uses same previous definded object – aah134 Jul 11 '13 at 21:27
0

You could use the function isobject() (see doc here) to check if the variable is an object. If true, you could then check the class of the object with class() (see doc here) and compare it to the class of the object you want to build. Something like (just to give you an idea):

if isobject(obj)
    if class(obj) == myclass
        % ask to replace it or not
    else
        % create new one over the object of a different class
    end
else
    % create new one
end

If I understand your question correctly, you probably want to put this as a constructor function for your class. I think you will have to pass the variable name when calling the constructor: obj = Obj(obj).

user2482876
  • 288
  • 4
  • 15
  • You could use `exist` instead of `isobject` as well – David K Jul 09 '13 at 19:08
  • That would work, except I'm really looking to use this for a default constructor, not a copy constructor. Is it possible for the constructor to check if the object is already created? By the time the constructor is called, I'm fairly sure a new object is already created. I'm trying to get around that! – Nick Sweet Jul 09 '13 at 19:18
  • I see your problem. You could maybe create a function file that will check for the existence of the object and then create one if needed. You probably won't be able to name the function the same way as the class tho. – user2482876 Jul 09 '13 at 19:22
  • That would work. Would I be too picky if I were looking for a more elegant solution? I'm working with multiple people, so I'd rather have them call the default constructor normally (since I don't want them to have to look around for what function to use to create the object) – Nick Sweet Jul 09 '13 at 19:25
  • I should note, my Obj class is a subclass of Handle. Is there anything I could do to add to Handle's constructor, or maybe work with events/listeners? I'm inexperienced with those things. – Nick Sweet Jul 09 '13 at 20:16
  • @user2482876 Return value of `class` is string so you need to change that line to `if strcmp(class(obj), 'myClass')`, however you can just use one `if` and use `isa` function as in `if isa(obj, 'myClass')` – Mohsen Nosratinia Jul 09 '13 at 22:43