【问题标题】:How to call appropriate subclass constructor inside base class constructor in MATLAB如何在 MATLAB 的基类构造函数中调用适当的子类构造函数
【发布时间】:2012-01-25 10:18:29
【问题描述】:

我正在尝试在 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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

【问题讨论】:

  • 您是否尝试从构造函数内部调用被基类覆盖的函数(例如,在对象完成构造之前)?在大多数语言中,这是非法的。

标签: oop matlab inheritance constructor


【解决方案1】:

您的解决方案是实用的,并且包含一些松散的 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 构造MyBaseClass 知道其详细信息的 obj 中现有对象的一部分。

通过注意当您输入 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 中没有允许您重用所有类型的数组创建代码的模板功能。最后,您的解决方案是有效的,减少了代码重复,但确实需要您的类中的一些不自然的行为。这只是您必须进行的交易。

【讨论】:

    猜你喜欢
    • 2014-02-19
    • 2018-05-25
    • 2018-03-31
    • 2016-07-19
    • 2018-07-16
    • 2018-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多