MATLAB Language
Объектно-ориентированное программирование
Поиск…
Определение класса
Класс можно определить с помощью classdef
в файле .m
с тем же именем, что и класс. Файл может содержать classdef
... end
блока и локальные функции для использования в методах класса.
Наиболее общее определение класса MATLAB имеет следующую структуру:
classdef (ClassAttribute = expression, ...) ClassName < ParentClass1 & ParentClass2 & ...
properties (PropertyAttributes)
PropertyName
end
methods (MethodAttributes)
function obj = methodName(obj,arg2,...)
...
end
end
events (EventAttributes)
EventName
end
enumeration
EnumName
end
end
Документация MATLAB: атрибуты класса, атрибуты свойств, атрибуты метода, атрибуты событий , ограничения класса перечисления .
Пример:
Класс под названием Car
можно определить в файле Car.m
как
classdef Car < handle % handle class so properties persist
properties
make
model
mileage = 0;
end
methods
function obj = Car(make, model)
obj.make = make;
obj.model = model;
end
function drive(obj, milesDriven)
obj.mileage = obj.mileage + milesDriven;
end
end
end
Обратите внимание, что конструктор - это метод с тем же именем, что и класс. <Конструктор - это специальный метод класса или структуры в объектно-ориентированном программировании, который инициализирует объект этого типа. Конструктор - это метод экземпляра, который обычно имеет то же имя, что и класс, и может использоваться для установки значений элементов объекта либо по умолчанию, либо по определенным пользователем значениям.>
Экземпляр этого класса может быть создан путем вызова конструктора;
>> myCar = Car('Ford', 'Mustang'); //creating an instance of car class
Вызов метода drive
увеличит пробег
>> myCar.mileage
ans =
0
>> myCar.drive(450);
>> myCar.mileage
ans =
450
Классы Value vs Handle
Классы в MATLAB подразделяются на две основные категории: классы значений и классы дескрипторов. Основное различие заключается в том, что при копировании экземпляра класса значений базовые данные копируются в новый экземпляр, тогда как для классов дескрипторов новый экземпляр указывает на исходные данные и изменение значений в новом экземпляре меняет их в оригинале. Класс может быть определен как дескриптор путем наследования класса handle
.
classdef valueClass
properties
data
end
end
а также
classdef handleClass < handle
properties
data
end
end
затем
>> v1 = valueClass;
>> v1.data = 5;
>> v2 = v1;
>> v2.data = 7;
>> v1.data
ans =
5
>> h1 = handleClass;
>> h1.data = 5;
>> h2 = h1;
>> h2.data = 7;
>> h1.data
ans =
7
Наследование классов и абстрактных классов
Отказ от ответственности: примеры, представленные здесь, предназначены только для того, чтобы показать использование абстрактных классов и наследования и, возможно, не обязательно иметь практическое применение. Кроме того, в MATLAB нет ничего, кроме полиморфного, и поэтому использование абстрактных классов ограничено. В этом примере показано, кто должен создавать класс, наследовать от другого класса и применять абстрактный класс для определения общего интерфейса.
Использование абстрактных классов довольно ограничено в MATLAB, но оно все же может пригодиться несколько раз.
Предположим, нам нужен регистратор сообщений. Мы могли бы создать класс, подобный приведенному ниже:
classdef ScreenLogger
properties(Access=protected)
scrh;
end
methods
function obj = ScreenLogger(screenhandler)
obj.scrh = screenhandler;
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
fprintf(obj.scrh, '%s\n', sprintf(varargin{:}));
end
end
end
end
Свойства и методы
Короче говоря, свойства имеют состояние объекта, тогда как методы похожи на интерфейс и определяют действия над объектами.
Свойство scrh
защищено. Вот почему он должен быть инициализирован в конструкторе. Существуют другие методы (getters) для доступа к этому свойству, но это не соответствует этому примеру. Свойства и методы могут быть доступны через переменную, содержащую ссылку на объект, с использованием точечной нотации, за которой следует имя метода или свойства:
mylogger = ScreenLogger(1); % OK
mylogger.LogMessage('My %s %d message', 'very', 1); % OK
mylogger.scrh = 2; % ERROR!!! Access denied
Свойства и методы могут быть общедоступными, частными или защищенными. В этом случае protected означает, что я получаю доступ к scrh
из унаследованного класса, но не извне. По умолчанию все свойства и методы являются общедоступными. Поэтому LogMessage()
может свободно использоваться вне определения класса. Также LogMessage
определяет интерфейс, означающий, что это то, что мы должны вызывать, когда мы хотим, чтобы объект регистрировал наши пользовательские сообщения.
заявка
Предположим, у меня есть сценарий, в котором я использую свой регистратор:
clc;
% ... a code
logger = ScreenLogger(1);
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
Если у меня есть несколько мест, где я использую один и тот же журнал, а затем хочу изменить его на нечто более сложное, например, написать сообщение в файле, мне придется создать другой объект:
classdef DeepLogger
properties(SetAccess=protected)
FileName
end
methods
function obj = DeepLogger(filename)
obj.FileName = filename;
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
и просто измените одну строку кода на это:
clc;
% ... a code
logger = DeepLogger('mymessages.log');
Вышеупомянутый метод просто откроет файл, добавит сообщение в конец файла и закроет его. На данный момент, чтобы быть в согласии с моим интерфейсом, мне нужно помнить, что имя метода - LogMessage()
но в равной степени это может быть что угодно. MATLAB может заставить разработчиков придерживаться одного и того же имени, используя абстрактные классы. Предположим, мы определяем общий интерфейс для любого регистратора:
classdef MessageLogger
methods(Abstract=true)
LogMessage(obj, varargin);
end
end
Теперь, если оба ScreenLogger
и DeepLogger
наследуются от этого класса, MATLAB будет генерировать ошибку, если LogMessage()
не определен. Абстрактные классы помогают создавать похожие классы, которые могут использовать один и тот же интерфейс.
Ради этого примера я сделаю несколько другое изменение. Я собираюсь предположить, что DeepLogger будет делать как сообщение регистрации на экране, так и в файле одновременно. Поскольку ScreenLogger
уже регистрирует сообщения на экране, я собираюсь наследовать DeepLogger
из ScreenLoggger
чтобы избежать повторения. ScreenLogger
не изменяется вообще, кроме первой строки:
classdef ScreenLogger < MessageLogger
// the rest of previous code
Тем не менее, DeepLogger
нуждается в дополнительных изменениях в методе LogMessage
:
classdef DeepLogger < MessageLogger & ScreenLogger
properties(SetAccess=protected)
FileName
Path
end
methods
function obj = DeepLogger(screenhandler, filename)
[path,filen,ext] = fileparts(filename);
obj.FileName = [filen ext];
pbj.Path = pathn;
obj = obj@ScreenLogger(screenhandler);
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
LogMessage@ScreenLogger(obj, varargin{:});
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
Во-первых, я просто инициализирую свойства в конструкторе. Во-вторых, поскольку этот класс наследуется от ScreenLogger
мне также нужно инициализировать этот объект parrent. Эта строка еще важнее, потому что конструктор ScreenLogger
требует одного параметра для инициализации собственного объекта. Эта строка:
obj = obj@ScreenLogger(screenhandler);
просто говорит: «Назовите конструктора ScreenLogger и активируйте его с помощью обработчика экрана». Здесь стоит отметить, что я определил scrh
как защищенный. Поэтому я мог бы получить доступ к этому свойству от DeepLogger
. Если свойство было определено как личное. Единственный способ его инициализации - использовать консуктора.
Другое изменение - в methods
раздела. Опять же, чтобы избежать повторения, я вызываю LogMessage()
из родительского класса для регистрации сообщения на экране. Если бы мне пришлось что-то менять, чтобы улучшить внесение изменений в экранный журнал, теперь я должен сделать это в одном месте. Код DeepLogger
такой же, как и часть DeepLogger
.
Поскольку этот класс также наследуется от абстрактного класса MessageLogger
я должен был убедиться, что LogMessage()
внутри DeepLogger
также определен. Наследовать от MessageLogger
здесь немного сложно. Я думаю, что случаи переопределения LogMessage
обязательны - думаю.
Что касается кода, в котором применяется регистратор, благодаря общему интерфейсу в классах, я могу быть уверенным, что это изменение в одной строке во всем коде не вызовет никаких проблем. Те же сообщения будут отображаться на экране, как и раньше, но дополнительно код будет записывать такие сообщения в файл.
clc;
% ... a code
logger = DeepLogger(1, 'mylogfile.log');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
Надеюсь, что эти примеры объясняют использование классов, использование наследования и использование абстрактных классов.
PS. Решение этой проблемы является одним из многих. Другим решением, менее сложным, было бы заставить ScreenLoger
быть компонентом другого регистратора, такого как FileLogger
и т. Д. ScreenLogger
будет храниться в одном из свойств. Его LogMessage
просто вызовет LogMessage
ScreenLogger
и покажет текст на экране. Я выбрал более сложный подход, чтобы показать, как классы работают в MATLAB. Пример кода ниже:
classdef DeepLogger < MessageLogger
properties(SetAccess=protected)
FileName
Path
ScrLogger
end
methods
function obj = DeepLogger(screenhandler, filename)
[path,filen,ext] = fileparts(filename);
obj.FileName = [filen ext];
obj.Path = pathn;
obj.ScrLogger = ScreenLogger(screenhandler);
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
obj.LogMessage(obj.ScrLogger, varargin{:}); % <-------- thechange here
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
Конструкторы
Конструктор - это особый метод в классе, который вызывается при создании экземпляра объекта. Это обычная функция MATLAB, которая принимает входные параметры, но также должна следовать определенным правилам .
Конструкторы не требуются, так как MATLAB создает по умолчанию. На практике, однако, это место для определения состояния объекта. Например, свойства могут быть ограничены указанием атрибутов . Затем конструктор может инициализировать такие свойства по умолчанию или пользовательские значения, которые на самом деле могут быть отправлены входными параметрами конструктора.
Вызов конструктора простого класса
Это простой класс Person
.
classdef Person
properties
name
surname
address
end
methods
function obj = Person(name,surname,address)
obj.name = name;
obj.surname = surname;
obj.address = address;
end
end
end
Имя конструктора совпадает с именем класса. Следовательно, конструкторы вызываются именем своего класса. Класс Person
может быть создан следующим образом :
>> p = Person('John','Smith','London')
p =
Person with properties:
name: 'John'
surname: 'Smith'
address: 'London'
Вызов конструктора дочернего класса
Классы могут быть унаследованы от родительских классов, если общие свойства или методы общего доступа. Когда класс унаследован от другого, вполне вероятно, что должен быть вызван конструктор родительского класса.
Member
класса наследуется от класса Person
поскольку Member
использует те же свойства, что и класс Person, но также добавляет payment
к его определению.
classdef Member < Person
properties
payment
end
methods
function obj = Member(name,surname,address,payment)
obj = obj@Person(name,surname,address);
obj.payment = payment;
end
end
end
Аналогично классу Person
, Member
создается путем вызова его конструктора:
>> m = Member('Adam','Woodcock','Manchester',20)
m =
Member with properties:
payment: 20
name: 'Adam'
surname: 'Woodcock'
address: 'Manchester'
Для конструктора Person
требуются три входных параметра. Member
должен уважать этот факт и поэтому вызывать конструктор класса Person
с тремя параметрами. Это выполняется по строке:
obj = obj@Person(name,surname,address);
В приведенном выше примере показан случай, когда дочерний класс нуждается в информации для своего родительского класса. Вот почему конструктор Member
требует четыре параметра: три для его родительского класса и один для себя.