Создать массив объектов без входных аргументов

Фон

У меня есть разные типы файлов, из которых я извлекаю и храню данные, например .csv и файлы базы данных. Обычно это большие файлы, и у меня уже есть соответствующие методы для извлечения из них данных.

Желаемое поведение

Давайте использовать класс textfile в качестве примера. Я хотел бы иметь возможность создать массив объектов textfile, где каждый элемент соответствует уникальному текстовому файлу. Я хотел бы иметь возможность вызывать textfile с одним аргументом или вообще без аргументов.

  1. Если я не передам никаких аргументов, я попаду на экран выбора файлов, чтобы выбрать файлы, и будет создан массив объектов, по одному элементу для каждого выбранного файла.

  2. Если передается один аргумент, я бы хотел, чтобы массив создавался в зависимости от того, что это был за аргумент. Если это был путь к каталогу, снова откройте экран выбора файла. Если это был массив ячеек с путями к файлам, создайте массив объектов из этих файлов.

Я хотел бы иметь возможность легко создавать больше классов, таких как textfile, которые имеют такое же базовое поведение.

Попытки до сих пор

Я определил суперкласс file, чтобы позаботиться об общем для всех файлов поведении. Это включает в себя такие вещи, как назначение таких свойств, как filename и extension. Все остальные мои классы являются подклассами file.

В конструкторе для file у меня есть два входных аргумента. Моя идея заключалась в том, что в соответствующих конструкторах подклассов я бы назвал конструктор file. Первым входным аргументом будет тип файла, например. txt, а вторым аргументом я выбрал при создании массива textfile. Для простоты предположим, что единственный тип arg, который я хочу обрабатывать, — это путь к каталогу, из которого можно выбирать файлы.

classdef textfile < file
    methods
        function textfileObject = textfile(arg)
            if nargin == 0
                arg = '';
            end
            textfileObject@file('txt',arg);
    end
end

Конструктор file, в зависимости от того, что такое arg, когда я создаю объекты textfile, генерирует массив объектов в соответствии с с использованием требования конструктора без входных аргументов.

classdef file < handle
    properties
        Path
    end
    methods
        function FileObject = file(FileType,arg)
            if nargin == 2
                FileList = file.SelectFiles(FileType,arg);
                FileObject(numel(FileList),1) = file;
                for filecount = 1:numel(FileObject)
                    FileObject(filecount,1).Path = FileList{filecount};
                end
            end
        end
    end
    methods(Static)
        function FileList = SelectFiles(DirectoryPath)
             % Some selection dialogs. Returns a cell array of filepaths
        end
    end
end

Это работает, когда arg является каталогом, потому что, когда массив объектов инициализируется в textfile без входного аргумента, arg устанавливается в '', что работает с остальными конструкторами.

Однако я хочу иметь возможность не иметь никаких входных аргументов при создании объектов textfile, но это не работает с правилами No Input Argument для конструкторов.

Вопрос

Есть ли способ создавать массивы объектов, в то время как

  1. вызов конструктора суперкласса и

  2. не используя никаких входных аргументов?

Я открыт для других решений, которые могут помочь с моей проблемой создания классов для каждого из моих типов файлов.

Решение

Оказывается, конструктор подкласса можно вызвать из суперкласса с помощью анонимной функции. По сути, вы можете вызвать конструктор подкласса без входных аргументов, а затем сгенерировать имена файлов в суперклассе перед повторным вызовом подкласса (рекурсивно) с этими именами файлов в качестве входных данных. Спасибо @Suever.

Конструктор подкласса будет просто:

classdef textfile < file
    methods
        function textfileObject = textfile(varargin)
            textfileObject@file('txt',varargin{:});
        end
    end
end

И в суперклассе:

classdef file < handle
    properties
        Path
    end
    methods
        function FileObject = file(FileType,varargin)

            % Subclass constructor handle
            Constructor = @(FilePath)feval(class(FileObject),FilePath);

            % No subclass arguments
            if nargin == 1
                FileList = file.SelectFiles(FileType,'');
                for a = 1:numel(FileList)
                    FileObject(a,1) = Constructor(FileList{a});
                end

            % One subclass argument
            elseif nargin == 2
                arg = varargin{1};
                if ischar(arg)
                    FileStruct = dir(arg);
                    if numel(FileStruct) == 1
                        FileObject.Path = arg;
                    end
                end

            % Too many subclass arguments
            else
                error('Subclasses of file only take one or no input arguments');
            end
        end
    end
end

person Community    schedule 02.08.2016    source источник
comment
Итак, вы хотите, чтобы вам предлагали выбрать файл, когда вы не вводите данные?   -  person Suever    schedule 02.08.2016
comment
@Suever Да, когда я не ввожу никаких входных данных, мне бы хотелось, чтобы мне было предложено выбрать файлы (по умолчанию из рабочего каталога), а затем будет создан массив объектов с каждым элементом, соответствующим выбранным файлам.   -  person    schedule 02.08.2016


Ответы (1)


Способ сделать это - рекурсивно вызвать конструктор вашего класса для создания массива объектов textfile.

classdef textfile < file

    methods
        function self = textfile(arg)
            if ~exist('arg', 'var')
                % Get list of files somehow
                [fnames, pname] = uigetfile('MultiSelect', 'on');
                filelist = fullfile(pname, fnames);

                % Call the constructor again for each file
                output = cellfun(@textfile, filelist, 'uniformoutput', 0);

                % Flatten the cell array of objects into an array of the right shape
                self = reshape([output{:}], size(filelist));
            else
                % Do default construction here
                self = self@file('txt', arg);
            end
        end
    end
end

Если вы хотите реализовать это в базовом классе, вы можете изменить рекурсивный вызов на что-то вроде следующего, который вызовет правильный конструктор подкласса из суперкласса file

constructor = @(varargin)feval(class(self), varargin{:});
output = cellfun(constructor, filelist, 'uniformoutput', 0);
person Suever    schedule 02.08.2016
comment
Спасибо за Ваш ответ! Это решает проблему для класса textfile. Единственная плохая вещь заключается в том, что это означает, что мне придется писать этот фрагмент кода в каждом классе, который у меня есть для данного типа файла, поэтому я подумал, что было бы неплохо иметь суперкласс file для обработки всего этого. Есть ли способ использовать ваш подход, когда я хочу использовать конструктор суперкласса file для обработки выбора файла? - person ; 02.08.2016
comment
@spaggy Я добавил пример того, как вы можете сделать это из суперкласса - person Suever; 02.08.2016
comment
Проблема в том, что я хочу вызвать конструктор суперкласса только один раз, но вызов конструктора суперкласса в подклассе не может быть выполнен в условном блоке. Это означает, что каждый раз, когда конструктор суперкласса вызывает подкласс (один раз для каждого файла в filelist согласно cellfun), также будет вызываться конструктор суперкласса.... - person ; 03.08.2016
comment
@Jens Почему конструктор суперкласса вызывается только один раз? - person Suever; 03.08.2016
comment
То, как я написал, означает, что выбор файла происходит в суперклассе. У меня есть статический метод, который выводит массив ячеек с именами файлов. Я сделал это, чтобы мне не пришлось повторять код для всех моих подклассов, когда дело доходит до выбора файла. Однако я открыт для других решений, если это не будет работать с требованием наличия пустого блока if nargin == 0 при создании массивов объектов. - person ; 03.08.2016
comment
Мне удалось заставить его работать, используя эту анонимную функцию с feval для конструктора. Блестящая вещь, спасибо! - person ; 04.08.2016