Как вызвать соответствующий конструктор подкласса внутри конструктора базового класса в MATLAB - PullRequest
3 голосов
/ 21 декабря 2011

Я пытаюсь использовать одиночное наследование в Matlab и написать конструктор базового класса, который позволяет создавать массивы объектов, включая пустые, и который наследуется подклассами.Я не могу понять, как это сделать, не используя какой-то невероятно неуклюжий код.Должен быть лучший способ.

В этом игрушечном примере мой базовый класс называется MyBaseClass, а мой подкласс называется MySubClass.Каждый из них может быть создан с одним числовым аргументом или без аргументов (в этом случае предполагается NaN).В примере с игрушкой мой подкласс тривиален и никак не расширяет поведение 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)), чтобы восстановить имя подкласса и создать код в строках для вызова в конструкторе базового класса.Это кажется неуклюжим и плохим.(И для меня несколько удивительно, что это возможно, но это так.) Я думаю, я мог бы дублировать больше логики между конструкторами MyBaseClass и MySubClass, но это также кажется неуклюжим и плохим, и упускает момент наследования.Есть ли лучший способ?

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 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 Ответ

2 голосов
/ 23 декабря 2011

Ваше решение является функциональным и включает в себя некоторую свободную типизацию MATLAB для достижения того, чего вы хотите.Однако получение чистого и структурированного ООП, вероятно, потребует потери некоторых функций, которые вы хотите.В то же время, лучший способ избежать дублирования кода - это шаблонные / универсальные классы контейнеров, но они не поддерживаются в MATLAB в настоящее время.

Ваш код отражает документацию MATLAB для Построение массивов в конструкторе и опирается на MATLAB, который является свободно типизированным языком, который позволяет вам без проблем преобразовывать объект в массив объектов.Использование этой мощной и гибкой функции MATLAB создает некоторые организационные проблемы и может подорвать ваши усилия по созданию чистого, объектно-ориентированного кода.Проблемы начинаются, потому что конструктор MyBaseClass не является истинным конструктором для MyBaseClass.

Википедия говорит :

"В объектно-ориентированном программировании конструктор (иногда сокращенный до ctor)в классе - это специальный тип подпрограммы, вызываемой при создании объекта. Он подготавливает новый объект для использования, часто принимая параметры, которые конструктор использует для установки любых переменных-членов, необходимых при первом создании объекта. Он называется конструктором.потому что он создает значения членов данных класса. "

Обратите внимание, что конструктор MyBaseClass не создает значения для членов объекта.Вместо этого это функция, которая устанавливает объект равным массиву объектов типа MyBaseClass и пытается установить их членов данных на какое-либо значение.Вы можете увидеть, где объект obj уничтожен при установке в массив здесь:

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

Это поведение особенно бесполезно, когда вы выводите MySubClass из MyBaseClass, потому что MyBaseClass не должен назначать новый объект переменной obj.---- MySubClass уже создал новый объект в obj и просто просит MyBaseClass построить часть существующего объекта в obj, для которой MyBaseClass знает подробности.

Определенную ясность можно получить, заметив, что при вводе конструктора для MyBaseClass и MySubClass переменная obj уже заполняется совершенно хорошим экземпляром класса.Хорошая практика ООП будет заключаться в том, чтобы сохранить этот исходный экземпляр, использовать его в конструкторе базового класса и действовать только для заполнения его членов в конструкторе ---- , а не , чтобы полностью перезаписать объект чем-то новым.Мой вывод заключается в том, чтобы не назначать obj массивом внутри MyBaseClass.Вместо этого я бы рекомендовал создать класс MyBaseClassArray, который создает массив объектов MyBaseClass.

К сожалению, вам также необходимо создать дубликат класса MySubClassArray, который создает массив объектов MySubClass.Такие языки, как C ++ и Java, обходят эту проблему дублирования кода с помощью шаблонов и шаблонов, соответственно, но MATLAB в настоящее время не поддерживает шаблоны в любой форме (http://www.mathworks.com/help/techdoc/matlab_oop/brqzfut-1.html). Без шаблонов нет хорошего способа избежать дублирования кода. Вы можете попытаться избежать некоторого дублированияпутем создания универсальной функции CreateClassArray, которая принимает имя строки класса для создания и аргументы конструктора для использования для каждого объекта - но теперь мы возвращаемся к коду, который выглядит как ваш оригинал. Единственное отличие состоит в том, что теперь у нас естьЧеткое разделение между классом массива и отдельными объектами. Правда в том, что, хотя MATLAB не поддерживает шаблоны, его гибкие классы и система типов позволяют вам использовать eval (), как вы должны изменять код и перезаписывать объект по желанию и создавать код, который действуетв целом по классам. Стоимость? Читабельность, скорость и чувство дискомфорта, которое вы испытывали, когда видели, как ваш базовый класс строит подкласс.

Короче говоря, вы использовали гибкость MATLAB для перезаписи obj в конструкторе массивом, чтобы избежать создания отдельного класса контейнера для MyBaseClass.Затем вы использовали eval, чтобы восполнить отсутствие в MATLAB функции шаблона, которая позволяла бы вам повторно использовать код создания массива всех типов.В конце концов, ваше решение является функциональным, уменьшает дублирование кода, но требует от ваших классов неестественного поведения.Это просто сделка, которую вы должны совершить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...