如何在MATLAB中调用基类构造函数中的相应子类构造函数

时间:2011-12-21 15:55:50

标签: oop matlab inheritance constructor

我正在尝试在Matlab中使用单继承,并编写一个基类构造函数,它允许创建对象数组,包括空数组,并由子类继承。如果不使用一些令人难以置信的笨重的代码,我无法弄清楚如何做到这一点。必须有更好的方法。

在这个玩具示例中,我的基类名为MyBaseClass,我的子类名为MySubClass。每个都可以用单个数字参数构造,或者不构造参数(在这种情况下假定为NaN)。在玩具示例中,我的SubClass是微不足道的,并没有以任何方式扩展MyBaseClass的行为,但显然在实践中它会做更多的事情。

我希望能够按如下方式调用每个构造函数:

obj = MyBaseClass;     % default constructor of 'NaN-like' object
obj = MyBaseClass([]); % create an empty 0x0 array of type MyBaseClass
obj = MyBaseClass(1);  % create a 1x1 array of MyBaseClass with value 1
obj = MyBaseClass([1 2; 3 4]) % create a 2x2 array of MyBaseClass with values 1, 2, 3, 4.

同样的四个调用MySubClass。

我发现的解决方案需要调用eval(class(obj))以恢复子类名称并构造字符串中的代码以便在基类构造函数中调用。这看起来很笨重而且很糟糕。 (这有点让我感到惊讶,它是可能的,但确实如此。)我想我可以在MyBaseClassMySubClass构造函数之间复制更多逻辑,但这看起来也很笨拙和糟糕,并且忽略了这一点继承。还有更好的方法吗?

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MyBaseClass.m

classdef MyBaseClass

    properties
        data = NaN        
    end

    methods

        % constructor
        function obj = MyBaseClass(varargin)
            if nargin == 0
                % Handle the no-argument case
                return
            end

            arg = varargin{1};

            % assume arg is a numeric array
            if isempty(arg)
                % Handle the case ClassName([])

                % Can't write this, because of subclasses:
                % obj = MyBaseClass.empty(size(arg));

                obj = eval([class(obj) '.empty(size(arg))']);
                return
            end

            % arg is an array
            % Make obj an array of the correct size by allocating the nth
            % element. Need to recurse for the no-argument case of the
            % relevant class constructor, which might not be this one.

            % Can't write this, because of subclasses
            % obj(numel(arg)) = MyBaseClass;

            obj(numel(arg)) = eval(class(obj));

            % Rest of the constructor - obviously in this toy example,
            % could be simplified.
            wh = ~isnan(arg);
            for i = find(wh(:))'
                obj(i).data = arg(i);
            end
            % And reshape to the size of the original
            obj = reshape(obj, size(arg));
        end

    end

end

% end of MyBaseClass.m
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MySubClass.m

classdef MySubClass < MyBaseClass

    methods
        function obj = MySubClass(varargin)
            obj = obj@MyBaseClass(varargin{:});
        end
    end 
end

% end of MySubClass.m
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

1 个答案:

答案 0 :(得分:2)

您的解决方案功能齐全,并且包含一些松散的MATLAB输入以实现您想要的效果。但是,获得干净和结构化的OOP可能需要丢失一些您想要的功能。同时,避免代码重复的最佳选择是模板化/通用容器类,但目前MATLAB不支持这些类。

您的代码反映了Building Arrays in the Constructor上的MATLAB文档,并依赖于MATLAB是一种松散类型的语言,使您能够毫无问题地将对象转换为对象数组。利用MATLAB强大而灵活的功能确实会引入一些组织问题,并可能会破坏您在干净的面向对象代码方面的努力。 问题开始是因为MyBaseClass构造函数不是MyBaseClass的真正构造函数。

维基百科says

“在面向对象的编程中,类中的构造函数(有时缩写为ctor)是在创建对象时调用的特殊类型的子例程。它准备新对象以供使用,通常接受构造函数使用的参数设置首次创建对象时所需的任何成员变量。它被称为构造函数,因为它构造了类的数据成员的值。“

请注意,MyBaseClass构造函数不构造对象成员的值。相反,它是一个函数,它将对象设置为MyBaseClass类型的对象数组,并尝试将数据成员设置为某个值。您可以在此处查看设置为数组时obj被销毁的位置:

obj(numel(arg)) = eval(class(obj));

当您从MyBaseClass派生MySubClass时,这种行为尤其无用,因为MyBaseClass不应该为变量obj分配新对象---- MySubClass已经在obj中创建了新对象,只是要求MyBaseClass构造obj中现有对象的一部分,MyBaseClass知道其详细信息。

通过注意当您输入MyBaseClass和MySubClass的构造函数时,可以获得一些清晰度,变量obj已经填充了一个非常好的类实例。好的OOP实践会让你保留这个原始实例,在基类构造函数中使用它,并且只在构造函数---- 而不是中填充其成员,以完全用新的东西覆盖对象。 我的结论是不将obj指定为MyBaseClass中的数组。相反,我建议创建一个MyBaseClassArray类,它创建一个MyBaseClass对象数组。

不幸的是,您还需要创建一个复制类MySubClassArray,它创建一个MySubClass对象数组。像C ++和Java这样的语言分别解决了模板和泛型的代码重复问题,但MATLAB目前不支持任何形式的模板(http://www.mathworks.com/help/techdoc/matlab_oop/brqzfut-1.html) 。没有模板就没有好办法避免代码重复。 您可以尝试通过创建一个通用的CreateClassArray函数来避免一些重复,该函数接受要创建的类的字符串名称以及用于每个对象的构造函数参数---但现在我们回到看起来像您原始的代码。唯一的区别是现在我们在数组类和单个对象之间有明确的区分。事实是,尽管MATLAB不支持模板,但它的灵活类和键入系统允许您使用eval(),就像您必须随意更改代码并覆盖obj并创建跨类操作的代码一样。成本?可见性,速度以及当您看到构建子类的基类时感觉不舒服。

简而言之,您使用MATLAB的灵活性来使用数组覆盖构造函数中的obj,以避免为MyBaseClass创建单独的容器类。然后,您使用eval来弥补MATLAB中没有模板功能,这将允许您重用所有类型的数组创建代码。最后,您的解决方案功能正常,减少了代码重复,但确实需要您的类中出现一些不自然的行为。这只是你必须做的交易。

相关问题